summaryrefslogtreecommitdiffstats
path: root/ipc/glue
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 /ipc/glue
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 'ipc/glue')
-rw-r--r--ipc/glue/BackgroundChild.h99
-rw-r--r--ipc/glue/BackgroundChildImpl.cpp518
-rw-r--r--ipc/glue/BackgroundChildImpl.h202
-rw-r--r--ipc/glue/BackgroundImpl.cpp2092
-rw-r--r--ipc/glue/BackgroundParent.h105
-rw-r--r--ipc/glue/BackgroundParentImpl.cpp848
-rw-r--r--ipc/glue/BackgroundParentImpl.h214
-rw-r--r--ipc/glue/BackgroundUtils.cpp382
-rw-r--r--ipc/glue/BackgroundUtils.h107
-rw-r--r--ipc/glue/BrowserProcessSubThread.cpp95
-rw-r--r--ipc/glue/BrowserProcessSubThread.h76
-rw-r--r--ipc/glue/CrashReporterClient.cpp66
-rw-r--r--ipc/glue/CrashReporterClient.h76
-rw-r--r--ipc/glue/CrashReporterHost.cpp128
-rw-r--r--ipc/glue/CrashReporterHost.h63
-rw-r--r--ipc/glue/CrashReporterMetadataShmem.cpp235
-rw-r--r--ipc/glue/CrashReporterMetadataShmem.h48
-rw-r--r--ipc/glue/CrossProcessMutex.h115
-rw-r--r--ipc/glue/CrossProcessMutex_posix.cpp144
-rw-r--r--ipc/glue/CrossProcessMutex_unimplemented.cpp47
-rw-r--r--ipc/glue/CrossProcessMutex_windows.cpp77
-rw-r--r--ipc/glue/Faulty.cpp654
-rw-r--r--ipc/glue/Faulty.h100
-rw-r--r--ipc/glue/FileDescriptor.cpp226
-rw-r--r--ipc/glue/FileDescriptor.h140
-rw-r--r--ipc/glue/FileDescriptorSetChild.cpp40
-rw-r--r--ipc/glue/FileDescriptorSetChild.h51
-rw-r--r--ipc/glue/FileDescriptorSetParent.cpp45
-rw-r--r--ipc/glue/FileDescriptorSetParent.h53
-rw-r--r--ipc/glue/FileDescriptorUtils.cpp126
-rw-r--r--ipc/glue/FileDescriptorUtils.h61
-rw-r--r--ipc/glue/GeckoChildProcessHost.cpp1268
-rw-r--r--ipc/glue/GeckoChildProcessHost.h223
-rw-r--r--ipc/glue/IOThreadChild.h49
-rw-r--r--ipc/glue/IPCMessageUtils.cpp23
-rw-r--r--ipc/glue/IPCMessageUtils.h830
-rw-r--r--ipc/glue/IPCStream.ipdlh35
-rw-r--r--ipc/glue/IPCStreamUtils.cpp495
-rw-r--r--ipc/glue/IPCStreamUtils.h185
-rw-r--r--ipc/glue/InputStreamParams.ipdlh96
-rw-r--r--ipc/glue/InputStreamUtils.cpp192
-rw-r--r--ipc/glue/InputStreamUtils.h41
-rw-r--r--ipc/glue/MessageChannel.cpp2560
-rw-r--r--ipc/glue/MessageChannel.h718
-rw-r--r--ipc/glue/MessageLink.cpp387
-rw-r--r--ipc/glue/MessageLink.h133
-rw-r--r--ipc/glue/MessagePump.cpp465
-rw-r--r--ipc/glue/MessagePump.h170
-rw-r--r--ipc/glue/Neutering.h79
-rw-r--r--ipc/glue/PBackground.ipdl122
-rw-r--r--ipc/glue/PBackgroundSharedTypes.ipdlh58
-rw-r--r--ipc/glue/PBackgroundTest.ipdl20
-rw-r--r--ipc/glue/PFileDescriptorSet.ipdl23
-rw-r--r--ipc/glue/PSendStream.ipdl33
-rw-r--r--ipc/glue/ProcessChild.cpp52
-rw-r--r--ipc/glue/ProcessChild.h66
-rw-r--r--ipc/glue/ProcessUtils.h21
-rw-r--r--ipc/glue/ProcessUtils_bsd.cpp28
-rw-r--r--ipc/glue/ProcessUtils_linux.cpp24
-rw-r--r--ipc/glue/ProcessUtils_mac.mm20
-rw-r--r--ipc/glue/ProcessUtils_none.cpp18
-rw-r--r--ipc/glue/ProtocolTypes.ipdlh21
-rw-r--r--ipc/glue/ProtocolUtils.cpp720
-rw-r--r--ipc/glue/ProtocolUtils.h783
-rw-r--r--ipc/glue/ScopedXREEmbed.cpp120
-rw-r--r--ipc/glue/ScopedXREEmbed.h35
-rw-r--r--ipc/glue/SendStream.h101
-rw-r--r--ipc/glue/SendStreamAlloc.h21
-rw-r--r--ipc/glue/SendStreamChild.cpp429
-rw-r--r--ipc/glue/SendStreamParent.cpp136
-rw-r--r--ipc/glue/SharedMemory.cpp98
-rw-r--r--ipc/glue/SharedMemory.h152
-rw-r--r--ipc/glue/SharedMemoryBasic.h18
-rw-r--r--ipc/glue/SharedMemoryBasic_android.cpp139
-rw-r--r--ipc/glue/SharedMemoryBasic_android.h72
-rw-r--r--ipc/glue/SharedMemoryBasic_chromium.h99
-rw-r--r--ipc/glue/SharedMemoryBasic_mach.h84
-rw-r--r--ipc/glue/SharedMemoryBasic_mach.mm676
-rw-r--r--ipc/glue/SharedMemory_posix.cpp37
-rw-r--r--ipc/glue/SharedMemory_windows.cpp39
-rw-r--r--ipc/glue/Shmem.cpp497
-rw-r--r--ipc/glue/Shmem.h304
-rw-r--r--ipc/glue/StringUtil.cpp94
-rw-r--r--ipc/glue/TaskFactory.h110
-rw-r--r--ipc/glue/Transport.h45
-rw-r--r--ipc/glue/Transport_posix.cpp93
-rw-r--r--ipc/glue/Transport_posix.h44
-rw-r--r--ipc/glue/Transport_win.cpp125
-rw-r--r--ipc/glue/Transport_win.h111
-rw-r--r--ipc/glue/URIParams.ipdlh112
-rw-r--r--ipc/glue/URIUtils.cpp154
-rw-r--r--ipc/glue/URIUtils.h34
-rw-r--r--ipc/glue/WindowsMessageLoop.cpp1553
-rw-r--r--ipc/glue/WindowsMessageLoop.h168
-rw-r--r--ipc/glue/moz.build207
-rw-r--r--ipc/glue/nsIIPCBackgroundChildCreateCallback.h72
-rw-r--r--ipc/glue/nsIIPCSerializableInputStream.h114
-rw-r--r--ipc/glue/nsIIPCSerializableURI.h59
98 files changed, 23343 insertions, 0 deletions
diff --git a/ipc/glue/BackgroundChild.h b/ipc/glue/BackgroundChild.h
new file mode 100644
index 000000000..16c444313
--- /dev/null
+++ b/ipc/glue/BackgroundChild.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_backgroundchild_h__
+#define mozilla_ipc_backgroundchild_h__
+
+#include "base/process.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/Transport.h"
+
+class nsIDOMBlob;
+class nsIIPCBackgroundChildCreateCallback;
+
+namespace mozilla {
+namespace dom {
+
+class BlobImpl;
+class ContentChild;
+class ContentParent;
+class PBlobChild;
+
+} // namespace dom
+
+namespace ipc {
+
+class PBackgroundChild;
+
+// This class allows access to the PBackground protocol. PBackground allows
+// communication between any thread (in the parent or a child process) and a
+// single background thread in the parent process. Each PBackgroundChild
+// instance is tied to the thread on which it is created and must not be shared
+// across threads. Each PBackgroundChild is unique and valid as long as its
+// designated thread lives.
+//
+// Creation of PBackground is asynchronous. GetForCurrentThread() will return
+// null until the sequence is complete. GetOrCreateForCurrentThread() will start
+// the creation sequence and will call back via the
+// nsIIPCBackgroundChildCreateCallback interface when completed. Thereafter
+// (assuming success) GetForCurrentThread() will return the same actor every
+// time. SynchronouslyCreateForCurrentThread() will spin the event loop until
+// the BackgroundChild until the creation sequence is complete.
+//
+// CloseForCurrentThread() will close the current PBackground actor. Subsequent
+// calls to GetForCurrentThread will return null. CloseForCurrentThread() may
+// only be called exactly once for each thread-specific actor. Currently it is
+// illegal to call this before the PBackground actor has been created.
+//
+// The PBackgroundChild actor and all its sub-protocol actors will be
+// automatically destroyed when its designated thread completes.
+class BackgroundChild final
+{
+ friend class mozilla::dom::ContentChild;
+ friend class mozilla::dom::ContentParent;
+
+ typedef base::ProcessId ProcessId;
+ typedef mozilla::ipc::Transport Transport;
+
+public:
+ // See above.
+ static PBackgroundChild*
+ GetForCurrentThread();
+
+ // See above.
+ static bool
+ GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
+
+ // See above.
+ static PBackgroundChild*
+ SynchronouslyCreateForCurrentThread();
+
+ static mozilla::dom::PBlobChild*
+ GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
+ nsIDOMBlob* aBlob);
+
+ static mozilla::dom::PBlobChild*
+ GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
+ mozilla::dom::BlobImpl* aBlobImpl);
+
+ // See above.
+ static void
+ CloseForCurrentThread();
+
+private:
+ // Only called by ContentChild or ContentParent.
+ static void
+ Startup();
+
+ // Only called by ContentChild.
+ static PBackgroundChild*
+ Alloc(Transport* aTransport, ProcessId aOtherProcess);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundchild_h__
diff --git a/ipc/glue/BackgroundChildImpl.cpp b/ipc/glue/BackgroundChildImpl.cpp
new file mode 100644
index 000000000..b157048a4
--- /dev/null
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -0,0 +1,518 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "BackgroundChildImpl.h"
+
+#include "ActorsChild.h" // IndexedDB
+#include "BroadcastChannelChild.h"
+#include "ServiceWorkerManagerChild.h"
+#include "FileDescriptorSetChild.h"
+#ifdef MOZ_WEBRTC
+#include "CamerasChild.h"
+#endif
+#include "mozilla/media/MediaChild.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/PBlobChild.h"
+#include "mozilla/dom/PFileSystemRequestChild.h"
+#include "mozilla/dom/FileSystemTaskBase.h"
+#include "mozilla/dom/asmjscache/AsmJSCache.h"
+#include "mozilla/dom/cache/ActorUtils.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/quota/PQuotaChild.h"
+#ifdef MOZ_GAMEPAD
+#include "mozilla/dom/GamepadEventChannelChild.h"
+#include "mozilla/dom/GamepadTestChannelChild.h"
+#endif
+#include "mozilla/dom/MessagePortChild.h"
+#include "mozilla/ipc/PBackgroundTestChild.h"
+#include "mozilla/ipc/PSendStreamChild.h"
+#include "mozilla/layout/VsyncChild.h"
+#include "mozilla/net/PUDPSocketChild.h"
+#include "mozilla/dom/network/UDPSocketChild.h"
+#include "nsID.h"
+#include "nsTraceRefcnt.h"
+
+namespace {
+
+class TestChild final : public mozilla::ipc::PBackgroundTestChild
+{
+ friend class mozilla::ipc::BackgroundChildImpl;
+
+ nsCString mTestArg;
+
+ explicit TestChild(const nsCString& aTestArg)
+ : mTestArg(aTestArg)
+ {
+ MOZ_COUNT_CTOR(TestChild);
+ }
+
+protected:
+ ~TestChild()
+ {
+ MOZ_COUNT_DTOR(TestChild);
+ }
+
+public:
+ virtual bool
+ Recv__delete__(const nsCString& aTestArg) override;
+};
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+
+using mozilla::dom::UDPSocketChild;
+using mozilla::net::PUDPSocketChild;
+
+using mozilla::dom::asmjscache::PAsmJSCacheEntryChild;
+using mozilla::dom::cache::PCacheChild;
+using mozilla::dom::cache::PCacheStorageChild;
+using mozilla::dom::cache::PCacheStreamControlChild;
+
+// -----------------------------------------------------------------------------
+// BackgroundChildImpl::ThreadLocal
+// -----------------------------------------------------------------------------
+
+BackgroundChildImpl::
+ThreadLocal::ThreadLocal()
+ : mCurrentFileHandle(nullptr)
+{
+ // May happen on any thread!
+ MOZ_COUNT_CTOR(mozilla::ipc::BackgroundChildImpl::ThreadLocal);
+}
+
+BackgroundChildImpl::
+ThreadLocal::~ThreadLocal()
+{
+ // May happen on any thread!
+ MOZ_COUNT_DTOR(mozilla::ipc::BackgroundChildImpl::ThreadLocal);
+}
+
+// -----------------------------------------------------------------------------
+// BackgroundChildImpl
+// -----------------------------------------------------------------------------
+
+BackgroundChildImpl::BackgroundChildImpl()
+{
+ // May happen on any thread!
+ MOZ_COUNT_CTOR(mozilla::ipc::BackgroundChildImpl);
+}
+
+BackgroundChildImpl::~BackgroundChildImpl()
+{
+ // May happen on any thread!
+ MOZ_COUNT_DTOR(mozilla::ipc::BackgroundChildImpl);
+}
+
+void
+BackgroundChildImpl::ProcessingError(Result aCode, const char* aReason)
+{
+ // May happen on any thread!
+
+ nsAutoCString abortMessage;
+
+ switch (aCode) {
+
+#define HANDLE_CASE(_result) \
+ case _result: \
+ abortMessage.AssignLiteral(#_result); \
+ break
+
+ HANDLE_CASE(MsgDropped);
+ HANDLE_CASE(MsgNotKnown);
+ HANDLE_CASE(MsgNotAllowed);
+ HANDLE_CASE(MsgPayloadError);
+ HANDLE_CASE(MsgProcessingError);
+ HANDLE_CASE(MsgRouteError);
+ HANDLE_CASE(MsgValueError);
+
+#undef HANDLE_CASE
+
+ default:
+ MOZ_CRASH("Unknown error code!");
+ }
+
+ MOZ_CRASH_UNSAFE_PRINTF("%s: %s", abortMessage.get(), aReason);
+}
+
+void
+BackgroundChildImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // May happen on any thread!
+}
+
+PBackgroundTestChild*
+BackgroundChildImpl::AllocPBackgroundTestChild(const nsCString& aTestArg)
+{
+ return new TestChild(aTestArg);
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundTestChild(PBackgroundTestChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<TestChild*>(aActor);
+ return true;
+}
+
+BackgroundChildImpl::PBackgroundIDBFactoryChild*
+BackgroundChildImpl::AllocPBackgroundIDBFactoryChild(
+ const LoggingInfo& aLoggingInfo)
+{
+ MOZ_CRASH("PBackgroundIDBFactoryChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundIDBFactoryChild(
+ PBackgroundIDBFactoryChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete aActor;
+ return true;
+}
+
+BackgroundChildImpl::PBackgroundIndexedDBUtilsChild*
+BackgroundChildImpl::AllocPBackgroundIndexedDBUtilsChild()
+{
+ MOZ_CRASH("PBackgroundIndexedDBUtilsChild actors should be manually "
+ "constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundIndexedDBUtilsChild(
+ PBackgroundIndexedDBUtilsChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete aActor;
+ return true;
+}
+
+auto
+BackgroundChildImpl::AllocPBlobChild(const BlobConstructorParams& aParams)
+ -> PBlobChild*
+{
+ MOZ_ASSERT(aParams.type() != BlobConstructorParams::T__None);
+
+ return mozilla::dom::BlobChild::Create(this, aParams);
+}
+
+bool
+BackgroundChildImpl::DeallocPBlobChild(PBlobChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ mozilla::dom::BlobChild::Destroy(aActor);
+ return true;
+}
+
+PFileDescriptorSetChild*
+BackgroundChildImpl::AllocPFileDescriptorSetChild(
+ const FileDescriptor& aFileDescriptor)
+{
+ return new FileDescriptorSetChild(aFileDescriptor);
+}
+
+bool
+BackgroundChildImpl::DeallocPFileDescriptorSetChild(
+ PFileDescriptorSetChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<FileDescriptorSetChild*>(aActor);
+ return true;
+}
+
+BackgroundChildImpl::PVsyncChild*
+BackgroundChildImpl::AllocPVsyncChild()
+{
+ RefPtr<mozilla::layout::VsyncChild> actor = new mozilla::layout::VsyncChild();
+ // There still has one ref-count after return, and it will be released in
+ // DeallocPVsyncChild().
+ return actor.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPVsyncChild(PVsyncChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ // This actor already has one ref-count. Please check AllocPVsyncChild().
+ RefPtr<mozilla::layout::VsyncChild> actor =
+ dont_AddRef(static_cast<mozilla::layout::VsyncChild*>(aActor));
+ return true;
+}
+
+PUDPSocketChild*
+BackgroundChildImpl::AllocPUDPSocketChild(const OptionalPrincipalInfo& aPrincipalInfo,
+ const nsCString& aFilter)
+{
+ MOZ_CRASH("AllocPUDPSocket should not be called");
+ return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPUDPSocketChild(PUDPSocketChild* child)
+{
+
+ UDPSocketChild* p = static_cast<UDPSocketChild*>(child);
+ p->ReleaseIPDLReference();
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// BroadcastChannel API
+// -----------------------------------------------------------------------------
+
+dom::PBroadcastChannelChild*
+BackgroundChildImpl::AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
+ const nsCString& aOrigin,
+ const nsString& aChannel)
+{
+ RefPtr<dom::BroadcastChannelChild> agent =
+ new dom::BroadcastChannelChild(aOrigin);
+ return agent.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPBroadcastChannelChild(
+ PBroadcastChannelChild* aActor)
+{
+ RefPtr<dom::BroadcastChannelChild> child =
+ dont_AddRef(static_cast<dom::BroadcastChannelChild*>(aActor));
+ MOZ_ASSERT(child);
+ return true;
+}
+
+camera::PCamerasChild*
+BackgroundChildImpl::AllocPCamerasChild()
+{
+#ifdef MOZ_WEBRTC
+ RefPtr<camera::CamerasChild> agent =
+ new camera::CamerasChild();
+ return agent.forget().take();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+BackgroundChildImpl::DeallocPCamerasChild(camera::PCamerasChild *aActor)
+{
+#ifdef MOZ_WEBRTC
+ RefPtr<camera::CamerasChild> child =
+ dont_AddRef(static_cast<camera::CamerasChild*>(aActor));
+ MOZ_ASSERT(aActor);
+#endif
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// ServiceWorkerManager
+// -----------------------------------------------------------------------------
+
+dom::PServiceWorkerManagerChild*
+BackgroundChildImpl::AllocPServiceWorkerManagerChild()
+{
+ RefPtr<dom::workers::ServiceWorkerManagerChild> agent =
+ new dom::workers::ServiceWorkerManagerChild();
+ return agent.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPServiceWorkerManagerChild(
+ PServiceWorkerManagerChild* aActor)
+{
+ RefPtr<dom::workers::ServiceWorkerManagerChild> child =
+ dont_AddRef(static_cast<dom::workers::ServiceWorkerManagerChild*>(aActor));
+ MOZ_ASSERT(child);
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// Cache API
+// -----------------------------------------------------------------------------
+
+PCacheStorageChild*
+BackgroundChildImpl::AllocPCacheStorageChild(const Namespace& aNamespace,
+ const PrincipalInfo& aPrincipalInfo)
+{
+ MOZ_CRASH("CacheStorageChild actor must be provided to PBackground manager");
+ return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPCacheStorageChild(PCacheStorageChild* aActor)
+{
+ dom::cache::DeallocPCacheStorageChild(aActor);
+ return true;
+}
+
+PCacheChild*
+BackgroundChildImpl::AllocPCacheChild()
+{
+ return dom::cache::AllocPCacheChild();
+}
+
+bool
+BackgroundChildImpl::DeallocPCacheChild(PCacheChild* aActor)
+{
+ dom::cache::DeallocPCacheChild(aActor);
+ return true;
+}
+
+PCacheStreamControlChild*
+BackgroundChildImpl::AllocPCacheStreamControlChild()
+{
+ return dom::cache::AllocPCacheStreamControlChild();
+}
+
+bool
+BackgroundChildImpl::DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor)
+{
+ dom::cache::DeallocPCacheStreamControlChild(aActor);
+ return true;
+}
+
+// -----------------------------------------------------------------------------
+// MessageChannel/MessagePort API
+// -----------------------------------------------------------------------------
+
+dom::PMessagePortChild*
+BackgroundChildImpl::AllocPMessagePortChild(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ RefPtr<dom::MessagePortChild> agent = new dom::MessagePortChild();
+ return agent.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPMessagePortChild(PMessagePortChild* aActor)
+{
+ RefPtr<dom::MessagePortChild> child =
+ dont_AddRef(static_cast<dom::MessagePortChild*>(aActor));
+ MOZ_ASSERT(child);
+ return true;
+}
+
+PSendStreamChild*
+BackgroundChildImpl::AllocPSendStreamChild()
+{
+ MOZ_CRASH("PSendStreamChild actors should be manually constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPSendStreamChild(PSendStreamChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+PAsmJSCacheEntryChild*
+BackgroundChildImpl::AllocPAsmJSCacheEntryChild(
+ const dom::asmjscache::OpenMode& aOpenMode,
+ const dom::asmjscache::WriteParams& aWriteParams,
+ const PrincipalInfo& aPrincipalInfo)
+{
+ MOZ_CRASH("PAsmJSCacheEntryChild actors should be manually constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ dom::asmjscache::DeallocEntryChild(aActor);
+ return true;
+}
+
+BackgroundChildImpl::PQuotaChild*
+BackgroundChildImpl::AllocPQuotaChild()
+{
+ MOZ_CRASH("PQuotaChild actor should be manually constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPQuotaChild(PQuotaChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ delete aActor;
+ return true;
+}
+
+dom::PFileSystemRequestChild*
+BackgroundChildImpl::AllocPFileSystemRequestChild(const FileSystemParams& aParams)
+{
+ MOZ_CRASH("Should never get here!");
+ return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aActor)
+{
+ // The reference is increased in FileSystemTaskBase::Start of
+ // FileSystemTaskBase.cpp. We should decrease it after IPC.
+ RefPtr<dom::FileSystemTaskChildBase> child =
+ dont_AddRef(static_cast<dom::FileSystemTaskChildBase*>(aActor));
+ return true;
+}
+
+// Gamepad API Background IPC
+dom::PGamepadEventChannelChild*
+BackgroundChildImpl::AllocPGamepadEventChannelChild()
+{
+ MOZ_CRASH("PGamepadEventChannelChild actor should be manually constructed!");
+ return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor)
+{
+#ifdef MOZ_GAMEPAD
+ MOZ_ASSERT(aActor);
+ delete static_cast<dom::GamepadEventChannelChild*>(aActor);
+#endif
+ return true;
+}
+
+dom::PGamepadTestChannelChild*
+BackgroundChildImpl::AllocPGamepadTestChannelChild()
+{
+#ifdef MOZ_GAMEPAD
+ MOZ_CRASH("PGamepadTestChannelChild actor should be manually constructed!");
+#endif
+ return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor)
+{
+#ifdef MOZ_GAMEPAD
+ MOZ_ASSERT(aActor);
+ delete static_cast<dom::GamepadTestChannelChild*>(aActor);
+#endif
+ return true;
+}
+
+} // namespace ipc
+} // namespace mozilla
+
+bool
+TestChild::Recv__delete__(const nsCString& aTestArg)
+{
+ MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
+ "BackgroundTest message was corrupted!");
+
+ return true;
+}
diff --git a/ipc/glue/BackgroundChildImpl.h b/ipc/glue/BackgroundChildImpl.h
new file mode 100644
index 000000000..f8c2a5576
--- /dev/null
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_backgroundchildimpl_h__
+#define mozilla_ipc_backgroundchildimpl_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class FileHandleBase;
+
+namespace indexedDB {
+
+class ThreadLocal;
+
+} // namespace indexedDB
+} // namespace dom
+
+namespace ipc {
+
+// Instances of this class should never be created directly. This class is meant
+// to be inherited in BackgroundImpl.
+class BackgroundChildImpl : public PBackgroundChild
+{
+public:
+ class ThreadLocal;
+
+ // Get the ThreadLocal for the current thread if
+ // BackgroundChild::GetOrCreateForCurrentThread() has been called and true was
+ // returned (e.g. a valid PBackgroundChild actor has been created or is in the
+ // process of being created). Otherwise this function returns null.
+ // This functions is implemented in BackgroundImpl.cpp.
+ static ThreadLocal*
+ GetThreadLocalForCurrentThread();
+
+protected:
+ BackgroundChildImpl();
+ virtual ~BackgroundChildImpl();
+
+ virtual void
+ ProcessingError(Result aCode, const char* aReason) override;
+
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual PBackgroundTestChild*
+ AllocPBackgroundTestChild(const nsCString& aTestArg) override;
+
+ virtual bool
+ DeallocPBackgroundTestChild(PBackgroundTestChild* aActor) override;
+
+ virtual PBackgroundIDBFactoryChild*
+ AllocPBackgroundIDBFactoryChild(const LoggingInfo& aLoggingInfo) override;
+
+ virtual bool
+ DeallocPBackgroundIDBFactoryChild(PBackgroundIDBFactoryChild* aActor)
+ override;
+
+ virtual PBackgroundIndexedDBUtilsChild*
+ AllocPBackgroundIndexedDBUtilsChild() override;
+
+ virtual bool
+ DeallocPBackgroundIndexedDBUtilsChild(PBackgroundIndexedDBUtilsChild* aActor)
+ override;
+
+ virtual PBlobChild*
+ AllocPBlobChild(const BlobConstructorParams& aParams) override;
+
+ virtual bool
+ DeallocPBlobChild(PBlobChild* aActor) override;
+
+ virtual PFileDescriptorSetChild*
+ AllocPFileDescriptorSetChild(const FileDescriptor& aFileDescriptor)
+ override;
+
+ virtual bool
+ DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
+
+ virtual PCamerasChild*
+ AllocPCamerasChild() override;
+
+ virtual bool
+ DeallocPCamerasChild(PCamerasChild* aActor) override;
+
+ virtual PVsyncChild*
+ AllocPVsyncChild() override;
+
+ virtual bool
+ DeallocPVsyncChild(PVsyncChild* aActor) override;
+
+ virtual PUDPSocketChild*
+ AllocPUDPSocketChild(const OptionalPrincipalInfo& aPrincipalInfo,
+ const nsCString& aFilter) override;
+ virtual bool
+ DeallocPUDPSocketChild(PUDPSocketChild* aActor) override;
+
+ virtual PBroadcastChannelChild*
+ AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
+ const nsCString& aOrigin,
+ const nsString& aChannel) override;
+
+ virtual bool
+ DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) override;
+
+ virtual PServiceWorkerManagerChild*
+ AllocPServiceWorkerManagerChild() override;
+
+ virtual bool
+ DeallocPServiceWorkerManagerChild(PServiceWorkerManagerChild* aActor) override;
+
+ virtual dom::cache::PCacheStorageChild*
+ AllocPCacheStorageChild(const dom::cache::Namespace& aNamespace,
+ const PrincipalInfo& aPrincipalInfo) override;
+
+ virtual bool
+ DeallocPCacheStorageChild(dom::cache::PCacheStorageChild* aActor) override;
+
+ virtual dom::cache::PCacheChild* AllocPCacheChild() override;
+
+ virtual bool
+ DeallocPCacheChild(dom::cache::PCacheChild* aActor) override;
+
+ virtual dom::cache::PCacheStreamControlChild*
+ AllocPCacheStreamControlChild() override;
+
+ virtual bool
+ DeallocPCacheStreamControlChild(dom::cache::PCacheStreamControlChild* aActor) override;
+
+ virtual PMessagePortChild*
+ AllocPMessagePortChild(const nsID& aUUID, const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID) override;
+
+ virtual bool
+ DeallocPMessagePortChild(PMessagePortChild* aActor) override;
+
+ virtual PSendStreamChild*
+ AllocPSendStreamChild() override;
+
+ virtual bool
+ DeallocPSendStreamChild(PSendStreamChild* aActor) override;
+
+ virtual PAsmJSCacheEntryChild*
+ AllocPAsmJSCacheEntryChild(const dom::asmjscache::OpenMode& aOpenMode,
+ const dom::asmjscache::WriteParams& aWriteParams,
+ const PrincipalInfo& aPrincipalInfo) override;
+
+ virtual bool
+ DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor) override;
+
+ virtual PQuotaChild*
+ AllocPQuotaChild() override;
+
+ virtual bool
+ DeallocPQuotaChild(PQuotaChild* aActor) override;
+
+ virtual PFileSystemRequestChild*
+ AllocPFileSystemRequestChild(const FileSystemParams&) override;
+
+ virtual bool
+ DeallocPFileSystemRequestChild(PFileSystemRequestChild*) override;
+
+ // Gamepad API Background IPC
+ virtual PGamepadEventChannelChild*
+ AllocPGamepadEventChannelChild() override;
+
+ virtual bool
+ DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor) override;
+
+ virtual PGamepadTestChannelChild*
+ AllocPGamepadTestChannelChild() override;
+
+ virtual bool
+ DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor) override;
+};
+
+class BackgroundChildImpl::ThreadLocal final
+{
+ friend class nsAutoPtr<ThreadLocal>;
+
+public:
+ nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
+ mozilla::dom::FileHandleBase* mCurrentFileHandle;
+
+public:
+ ThreadLocal();
+
+private:
+ // Only destroyed by nsAutoPtr<ThreadLocal>.
+ ~ThreadLocal();
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundchildimpl_h__
diff --git a/ipc/glue/BackgroundImpl.cpp b/ipc/glue/BackgroundImpl.cpp
new file mode 100644
index 000000000..2f8e073f8
--- /dev/null
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -0,0 +1,2092 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "BackgroundChild.h"
+#include "BackgroundParent.h"
+
+#include "BackgroundChildImpl.h"
+#include "BackgroundParentImpl.h"
+#include "base/process_util.h"
+#include "base/task.h"
+#include "FileDescriptor.h"
+#include "GeckoProfiler.h"
+#include "InputStreamUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/ipc/nsIRemoteBlob.h"
+#include "mozilla/ipc/ProtocolTypes.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsIMutable.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIRunnable.h"
+#include "nsISupportsImpl.h"
+#include "nsIThread.h"
+#include "nsITimer.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+#include "nsXULAppAPI.h"
+#include "nsXPCOMPrivate.h"
+#include "prthread.h"
+
+#ifdef RELEASE_OR_BETA
+#define THREADSAFETY_ASSERT MOZ_ASSERT
+#else
+#define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT
+#endif
+
+#define CRASH_IN_CHILD_PROCESS(_msg) \
+ do { \
+ if (XRE_IsParentProcess()) { \
+ MOZ_ASSERT(false, _msg); \
+ } else { \
+ MOZ_CRASH(_msg); \
+ } \
+ } \
+ while (0)
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+namespace {
+
+// -----------------------------------------------------------------------------
+// Utility Functions
+// -----------------------------------------------------------------------------
+
+
+void
+AssertIsInMainProcess()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+}
+
+void
+AssertIsInChildProcess()
+{
+ MOZ_ASSERT(!XRE_IsParentProcess());
+}
+
+void
+AssertIsOnMainThread()
+{
+ THREADSAFETY_ASSERT(NS_IsMainThread());
+}
+
+// -----------------------------------------------------------------------------
+// ParentImpl Declaration
+// -----------------------------------------------------------------------------
+
+class ParentImpl final : public BackgroundParentImpl
+{
+ friend class mozilla::ipc::BackgroundParent;
+
+public:
+ class CreateCallback;
+
+private:
+ class ShutdownObserver;
+ class RequestMessageLoopRunnable;
+ class ShutdownBackgroundThreadRunnable;
+ class ForceCloseBackgroundActorsRunnable;
+ class CreateCallbackRunnable;
+ class ConnectActorRunnable;
+
+ struct MOZ_STACK_CLASS TimerCallbackClosure
+ {
+ nsIThread* mThread;
+ nsTArray<ParentImpl*>* mLiveActors;
+
+ TimerCallbackClosure(nsIThread* aThread, nsTArray<ParentImpl*>* aLiveActors)
+ : mThread(aThread), mLiveActors(aLiveActors)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aThread);
+ MOZ_ASSERT(aLiveActors);
+ }
+ };
+
+ // The length of time we will wait at shutdown for all actors to clean
+ // themselves up before forcing them to be destroyed.
+ static const uint32_t kShutdownTimerDelayMS = 10000;
+
+ // This is only modified on the main thread. It is null if the thread does not
+ // exist or is shutting down.
+ static StaticRefPtr<nsIThread> sBackgroundThread;
+
+ // This is created and destroyed on the main thread but only modified on the
+ // background thread. It is specific to each instance of sBackgroundThread.
+ static nsTArray<ParentImpl*>* sLiveActorsForBackgroundThread;
+
+ // This is only modified on the main thread.
+ static StaticRefPtr<nsITimer> sShutdownTimer;
+
+ // This exists so that that [Assert]IsOnBackgroundThread() can continue to
+ // work during shutdown.
+ static Atomic<PRThread*> sBackgroundPRThread;
+
+ // This is only modified on the main thread. It is null if the thread does not
+ // exist or is shutting down.
+ static MessageLoop* sBackgroundThreadMessageLoop;
+
+ // This is only modified on the main thread. It maintains a count of live
+ // actors so that the background thread can be shut down when it is no longer
+ // needed.
+ static uint64_t sLiveActorCount;
+
+ // This is only modified on the main thread. It is true after the shutdown
+ // observer is registered and is never unset thereafter.
+ static bool sShutdownObserverRegistered;
+
+ // This is only modified on the main thread. It prevents us from trying to
+ // create the background thread after application shutdown has started.
+ static bool sShutdownHasStarted;
+
+ // This is only modified on the main thread. It is a FIFO queue for callbacks
+ // waiting for the background thread to be created.
+ static StaticAutoPtr<nsTArray<RefPtr<CreateCallback>>> sPendingCallbacks;
+
+ // Only touched on the main thread, null if this is a same-process actor.
+ RefPtr<ContentParent> mContent;
+
+ // Set when the actor is opened successfully and used to handle shutdown
+ // hangs. Only touched on the background thread.
+ nsTArray<ParentImpl*>* mLiveActorArray;
+
+ // Set at construction to indicate whether this parent actor corresponds to a
+ // child actor in another process or to a child actor from a different thread
+ // in the same process.
+ const bool mIsOtherProcessActor;
+
+ // Set after ActorDestroy has been called. Only touched on the background
+ // thread.
+ bool mActorDestroyed;
+
+public:
+ static bool
+ CreateActorForSameProcess(CreateCallback* aCallback);
+
+ static bool
+ IsOnBackgroundThread()
+ {
+ return PR_GetCurrentThread() == sBackgroundPRThread;
+ }
+
+ static void
+ AssertIsOnBackgroundThread()
+ {
+ THREADSAFETY_ASSERT(IsOnBackgroundThread());
+ }
+
+ NS_INLINE_DECL_REFCOUNTING(ParentImpl)
+
+ void
+ Destroy();
+
+private:
+ // Forwarded from BackgroundParent.
+ static bool
+ IsOtherProcessActor(PBackgroundParent* aBackgroundActor);
+
+ // Forwarded from BackgroundParent.
+ static already_AddRefed<ContentParent>
+ GetContentParent(PBackgroundParent* aBackgroundActor);
+
+ // Forwarded from BackgroundParent.
+ static intptr_t
+ GetRawContentParentForComparison(PBackgroundParent* aBackgroundActor);
+
+ // Forwarded from BackgroundParent.
+ static PBackgroundParent*
+ Alloc(ContentParent* aContent,
+ Transport* aTransport,
+ ProcessId aOtherPid);
+
+ static bool
+ CreateBackgroundThread();
+
+ static void
+ ShutdownBackgroundThread();
+
+ static void
+ ShutdownTimerCallback(nsITimer* aTimer, void* aClosure);
+
+ // For same-process actors.
+ ParentImpl()
+ : mLiveActorArray(nullptr), mIsOtherProcessActor(false),
+ mActorDestroyed(false)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ }
+
+ // For other-process actors.
+ explicit ParentImpl(ContentParent* aContent)
+ : mContent(aContent), mLiveActorArray(nullptr),
+ mIsOtherProcessActor(true), mActorDestroyed(false)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aContent);
+ }
+
+ ~ParentImpl()
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!mContent);
+ }
+
+ void
+ MainThreadActorDestroy();
+
+ void
+ SetLiveActorArray(nsTArray<ParentImpl*>* aLiveActorArray)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aLiveActorArray);
+ MOZ_ASSERT(!aLiveActorArray->Contains(this));
+ MOZ_ASSERT(!mLiveActorArray);
+ MOZ_ASSERT(mIsOtherProcessActor);
+
+ mLiveActorArray = aLiveActorArray;
+ mLiveActorArray->AppendElement(this);
+ }
+
+ // These methods are only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+// -----------------------------------------------------------------------------
+// ChildImpl Declaration
+// -----------------------------------------------------------------------------
+
+class ChildImpl final : public BackgroundChildImpl
+{
+ friend class mozilla::ipc::BackgroundChild;
+ friend class mozilla::ipc::BackgroundChildImpl;
+
+ typedef base::ProcessId ProcessId;
+ typedef mozilla::ipc::Transport Transport;
+
+ class ShutdownObserver;
+ class CreateActorRunnable;
+ class ParentCreateCallback;
+ class AlreadyCreatedCallbackRunnable;
+ class FailedCreateCallbackRunnable;
+ class OpenChildProcessActorRunnable;
+ class OpenMainProcessActorRunnable;
+
+ // A thread-local index that is not valid.
+ static const unsigned int kBadThreadLocalIndex =
+ static_cast<unsigned int>(-1);
+
+ // This is only modified on the main thread. It is the thread-local index that
+ // we use to store the BackgroundChild for each thread.
+ static unsigned int sThreadLocalIndex;
+
+ struct ThreadLocalInfo
+ {
+ explicit ThreadLocalInfo(nsIIPCBackgroundChildCreateCallback* aCallback)
+#ifdef DEBUG
+ : mClosed(false)
+#endif
+ {
+ mCallbacks.AppendElement(aCallback);
+ }
+
+ RefPtr<ChildImpl> mActor;
+ nsTArray<nsCOMPtr<nsIIPCBackgroundChildCreateCallback>> mCallbacks;
+ nsAutoPtr<BackgroundChildImpl::ThreadLocal> mConsumerThreadLocal;
+#ifdef DEBUG
+ bool mClosed;
+#endif
+ };
+
+ // This is only modified on the main thread. It is a FIFO queue for actors
+ // that are in the process of construction.
+ static StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> sPendingTargets;
+
+ // This is only modified on the main thread. It prevents us from trying to
+ // create the background thread after application shutdown has started.
+ static bool sShutdownHasStarted;
+
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+ nsIThread* mBoundThread;
+#endif
+
+#ifdef DEBUG
+ bool mActorDestroyed;
+#endif
+
+public:
+ static bool
+ OpenProtocolOnMainThread(nsIEventTarget* aEventTarget);
+
+ static void
+ Shutdown();
+
+ void
+ AssertIsOnBoundThread()
+ {
+ THREADSAFETY_ASSERT(mBoundThread);
+
+#ifdef RELEASE_OR_BETA
+ DebugOnly<bool> current;
+#else
+ bool current;
+#endif
+ THREADSAFETY_ASSERT(
+ NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(&current)));
+ THREADSAFETY_ASSERT(current);
+ }
+
+ void
+ AssertActorDestroyed()
+ {
+ MOZ_ASSERT(mActorDestroyed, "ChildImpl::ActorDestroy not called in time");
+ }
+
+ ChildImpl()
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+ : mBoundThread(nullptr)
+#endif
+#ifdef DEBUG
+ , mActorDestroyed(false)
+#endif
+ {
+ AssertIsOnMainThread();
+ }
+
+ NS_INLINE_DECL_REFCOUNTING(ChildImpl)
+
+private:
+ // Forwarded from BackgroundChild.
+ static void
+ Startup();
+
+ // Forwarded from BackgroundChild.
+ static PBackgroundChild*
+ Alloc(Transport* aTransport, ProcessId aOtherPid);
+
+ // Forwarded from BackgroundChild.
+ static PBackgroundChild*
+ GetForCurrentThread();
+
+ // Forwarded from BackgroundChild.
+ static bool
+ GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
+
+ // Forwarded from BackgroundChild.
+ static PBackgroundChild*
+ SynchronouslyCreateForCurrentThread();
+
+ // Forwarded from BackgroundChild.
+ static void
+ CloseForCurrentThread();
+
+ // Forwarded from BackgroundChildImpl.
+ static BackgroundChildImpl::ThreadLocal*
+ GetThreadLocalForCurrentThread();
+
+ static void
+ ThreadLocalDestructor(void* aThreadLocal)
+ {
+ auto threadLocalInfo = static_cast<ThreadLocalInfo*>(aThreadLocal);
+
+ if (threadLocalInfo) {
+ MOZ_ASSERT(threadLocalInfo->mClosed);
+
+ if (threadLocalInfo->mActor) {
+ threadLocalInfo->mActor->Close();
+ threadLocalInfo->mActor->AssertActorDestroyed();
+
+ // Since the actor is created on the main thread it must only
+ // be released on the main thread as well.
+ if (!NS_IsMainThread()) {
+ ChildImpl* actor;
+ threadLocalInfo->mActor.forget(&actor);
+
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor, &ChildImpl::Release)));
+ }
+ }
+ delete threadLocalInfo;
+ }
+ }
+
+ static void
+ DispatchFailureCallback(nsIEventTarget* aEventTarget);
+
+ // This class is reference counted.
+ ~ChildImpl()
+ {
+ AssertActorDestroyed();
+ }
+
+ void
+ SetBoundThread()
+ {
+ THREADSAFETY_ASSERT(!mBoundThread);
+
+#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
+ mBoundThread = NS_GetCurrentThread();
+#endif
+
+ THREADSAFETY_ASSERT(mBoundThread);
+ }
+
+ // Only called by IPDL.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+
+ static already_AddRefed<nsIIPCBackgroundChildCreateCallback>
+ GetNextCallback();
+};
+
+// -----------------------------------------------------------------------------
+// ParentImpl Helper Declarations
+// -----------------------------------------------------------------------------
+
+class ParentImpl::ShutdownObserver final : public nsIObserver
+{
+public:
+ ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ ~ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+};
+
+class ParentImpl::RequestMessageLoopRunnable final : public Runnable
+{
+ nsCOMPtr<nsIThread> mTargetThread;
+ MessageLoop* mMessageLoop;
+
+public:
+ explicit RequestMessageLoopRunnable(nsIThread* aTargetThread)
+ : mTargetThread(aTargetThread), mMessageLoop(nullptr)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aTargetThread);
+ }
+
+private:
+ ~RequestMessageLoopRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ShutdownBackgroundThreadRunnable final : public Runnable
+{
+public:
+ ShutdownBackgroundThreadRunnable()
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ }
+
+private:
+ ~ShutdownBackgroundThreadRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ForceCloseBackgroundActorsRunnable final : public Runnable
+{
+ nsTArray<ParentImpl*>* mActorArray;
+
+public:
+ explicit ForceCloseBackgroundActorsRunnable(nsTArray<ParentImpl*>* aActorArray)
+ : mActorArray(aActorArray)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aActorArray);
+ }
+
+private:
+ ~ForceCloseBackgroundActorsRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::CreateCallbackRunnable final : public Runnable
+{
+ RefPtr<CreateCallback> mCallback;
+
+public:
+ explicit CreateCallbackRunnable(CreateCallback* aCallback)
+ : mCallback(aCallback)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aCallback);
+ }
+
+private:
+ ~CreateCallbackRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ParentImpl::ConnectActorRunnable final : public Runnable
+{
+ RefPtr<ParentImpl> mActor;
+ Transport* mTransport;
+ ProcessId mOtherPid;
+ nsTArray<ParentImpl*>* mLiveActorArray;
+
+public:
+ ConnectActorRunnable(ParentImpl* aActor,
+ Transport* aTransport,
+ ProcessId aOtherPid,
+ nsTArray<ParentImpl*>* aLiveActorArray)
+ : mActor(aActor), mTransport(aTransport), mOtherPid(aOtherPid),
+ mLiveActorArray(aLiveActorArray)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aTransport);
+ MOZ_ASSERT(aLiveActorArray);
+ }
+
+private:
+ ~ConnectActorRunnable()
+ {
+ AssertIsInMainProcess();
+ }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class NS_NO_VTABLE ParentImpl::CreateCallback
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(CreateCallback)
+
+ virtual void
+ Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop) = 0;
+
+ virtual void
+ Failure() = 0;
+
+protected:
+ virtual ~CreateCallback()
+ { }
+};
+
+// -----------------------------------------------------------------------------
+// ChildImpl Helper Declarations
+// -----------------------------------------------------------------------------
+
+class ChildImpl::ShutdownObserver final : public nsIObserver
+{
+public:
+ ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ ~ShutdownObserver()
+ {
+ AssertIsOnMainThread();
+ }
+};
+
+class ChildImpl::CreateActorRunnable final : public Runnable
+{
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+public:
+ CreateActorRunnable()
+ : mEventTarget(NS_GetCurrentThread())
+ {
+ MOZ_ASSERT(mEventTarget);
+ }
+
+private:
+ ~CreateActorRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::ParentCreateCallback final :
+ public ParentImpl::CreateCallback
+{
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+public:
+ explicit ParentCreateCallback(nsIEventTarget* aEventTarget)
+ : mEventTarget(aEventTarget)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aEventTarget);
+ }
+
+private:
+ ~ParentCreateCallback()
+ { }
+
+ virtual void
+ Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop)
+ override;
+
+ virtual void
+ Failure() override;
+};
+
+// Must be cancelable in order to dispatch on active worker threads
+class ChildImpl::AlreadyCreatedCallbackRunnable final :
+ public CancelableRunnable
+{
+public:
+ AlreadyCreatedCallbackRunnable()
+ {
+ // May be created on any thread!
+ }
+
+protected:
+ virtual ~AlreadyCreatedCallbackRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+};
+
+class ChildImpl::FailedCreateCallbackRunnable final : public Runnable
+{
+public:
+ FailedCreateCallbackRunnable()
+ {
+ // May be created on any thread!
+ }
+
+protected:
+ virtual ~FailedCreateCallbackRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::OpenChildProcessActorRunnable final : public Runnable
+{
+ RefPtr<ChildImpl> mActor;
+ nsAutoPtr<Transport> mTransport;
+ ProcessId mOtherPid;
+
+public:
+ OpenChildProcessActorRunnable(already_AddRefed<ChildImpl>&& aActor,
+ Transport* aTransport,
+ ProcessId aOtherPid)
+ : mActor(aActor), mTransport(aTransport),
+ mOtherPid(aOtherPid)
+ {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(aTransport);
+ }
+
+private:
+ ~OpenChildProcessActorRunnable()
+ {
+ if (mTransport) {
+ CRASH_IN_CHILD_PROCESS("Leaking transport!");
+ Unused << mTransport.forget();
+ }
+ }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+class ChildImpl::OpenMainProcessActorRunnable final : public Runnable
+{
+ RefPtr<ChildImpl> mActor;
+ RefPtr<ParentImpl> mParentActor;
+ MessageLoop* mParentMessageLoop;
+
+public:
+ OpenMainProcessActorRunnable(already_AddRefed<ChildImpl>&& aChildActor,
+ already_AddRefed<ParentImpl> aParentActor,
+ MessageLoop* aParentMessageLoop)
+ : mActor(aChildActor), mParentActor(aParentActor),
+ mParentMessageLoop(aParentMessageLoop)
+ {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mParentActor);
+ MOZ_ASSERT(aParentMessageLoop);
+ }
+
+private:
+ ~OpenMainProcessActorRunnable()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+};
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+
+bool
+IsOnBackgroundThread()
+{
+ return ParentImpl::IsOnBackgroundThread();
+}
+
+#ifdef DEBUG
+
+void
+AssertIsOnBackgroundThread()
+{
+ ParentImpl::AssertIsOnBackgroundThread();
+}
+
+#endif // DEBUG
+
+} // namespace ipc
+} // namespace mozilla
+
+// -----------------------------------------------------------------------------
+// BackgroundParent Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+bool
+BackgroundParent::IsOtherProcessActor(PBackgroundParent* aBackgroundActor)
+{
+ return ParentImpl::IsOtherProcessActor(aBackgroundActor);
+}
+
+// static
+already_AddRefed<ContentParent>
+BackgroundParent::GetContentParent(PBackgroundParent* aBackgroundActor)
+{
+ return ParentImpl::GetContentParent(aBackgroundActor);
+}
+
+// static
+PBlobParent*
+BackgroundParent::GetOrCreateActorForBlobImpl(
+ PBackgroundParent* aBackgroundActor,
+ BlobImpl* aBlobImpl)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+ MOZ_ASSERT(aBlobImpl);
+
+ BlobParent* actor = BlobParent::GetOrCreate(aBackgroundActor, aBlobImpl);
+ if (NS_WARN_IF(!actor)) {
+ return nullptr;
+ }
+
+ return actor;
+}
+
+// static
+intptr_t
+BackgroundParent::GetRawContentParentForComparison(
+ PBackgroundParent* aBackgroundActor)
+{
+ return ParentImpl::GetRawContentParentForComparison(aBackgroundActor);
+}
+
+// static
+PBackgroundParent*
+BackgroundParent::Alloc(ContentParent* aContent,
+ Transport* aTransport,
+ ProcessId aOtherPid)
+{
+ return ParentImpl::Alloc(aContent, aTransport, aOtherPid);
+}
+
+// -----------------------------------------------------------------------------
+// BackgroundChild Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+void
+BackgroundChild::Startup()
+{
+ ChildImpl::Startup();
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::Alloc(Transport* aTransport, ProcessId aOtherPid)
+{
+ return ChildImpl::Alloc(aTransport, aOtherPid);
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::GetForCurrentThread()
+{
+ return ChildImpl::GetForCurrentThread();
+}
+
+// static
+bool
+BackgroundChild::GetOrCreateForCurrentThread(
+ nsIIPCBackgroundChildCreateCallback* aCallback)
+{
+ return ChildImpl::GetOrCreateForCurrentThread(aCallback);
+}
+
+// static
+PBackgroundChild*
+BackgroundChild::SynchronouslyCreateForCurrentThread()
+{
+ return ChildImpl::SynchronouslyCreateForCurrentThread();
+}
+
+// static
+PBlobChild*
+BackgroundChild::GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
+ nsIDOMBlob* aBlob)
+{
+ MOZ_ASSERT(aBlob);
+
+ RefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
+ MOZ_ASSERT(blobImpl);
+
+ return GetOrCreateActorForBlobImpl(aBackgroundActor, blobImpl);
+}
+
+// static
+PBlobChild*
+BackgroundChild::GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
+ BlobImpl* aBlobImpl)
+{
+ MOZ_ASSERT(aBackgroundActor);
+ MOZ_ASSERT(aBlobImpl);
+ MOZ_ASSERT(GetForCurrentThread(),
+ "BackgroundChild not created on this thread yet!");
+ MOZ_ASSERT(aBackgroundActor == GetForCurrentThread(),
+ "BackgroundChild is bound to a different thread!");
+
+ BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, aBlobImpl);
+ if (NS_WARN_IF(!actor)) {
+ return nullptr;
+ }
+
+ return actor;
+}
+
+// static
+void
+BackgroundChild::CloseForCurrentThread()
+{
+ ChildImpl::CloseForCurrentThread();
+}
+
+// -----------------------------------------------------------------------------
+// BackgroundChildImpl Public Methods
+// -----------------------------------------------------------------------------
+
+// static
+BackgroundChildImpl::ThreadLocal*
+BackgroundChildImpl::GetThreadLocalForCurrentThread()
+{
+ return ChildImpl::GetThreadLocalForCurrentThread();
+}
+
+// -----------------------------------------------------------------------------
+// ParentImpl Static Members
+// -----------------------------------------------------------------------------
+
+StaticRefPtr<nsIThread> ParentImpl::sBackgroundThread;
+
+nsTArray<ParentImpl*>* ParentImpl::sLiveActorsForBackgroundThread;
+
+StaticRefPtr<nsITimer> ParentImpl::sShutdownTimer;
+
+Atomic<PRThread*> ParentImpl::sBackgroundPRThread;
+
+MessageLoop* ParentImpl::sBackgroundThreadMessageLoop = nullptr;
+
+uint64_t ParentImpl::sLiveActorCount = 0;
+
+bool ParentImpl::sShutdownObserverRegistered = false;
+
+bool ParentImpl::sShutdownHasStarted = false;
+
+StaticAutoPtr<nsTArray<RefPtr<ParentImpl::CreateCallback>>>
+ ParentImpl::sPendingCallbacks;
+
+// -----------------------------------------------------------------------------
+// ChildImpl Static Members
+// -----------------------------------------------------------------------------
+
+unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex;
+
+StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> ChildImpl::sPendingTargets;
+
+bool ChildImpl::sShutdownHasStarted = false;
+
+// -----------------------------------------------------------------------------
+// ParentImpl Implementation
+// -----------------------------------------------------------------------------
+
+// static
+bool
+ParentImpl::IsOtherProcessActor(PBackgroundParent* aBackgroundActor)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+
+ return static_cast<ParentImpl*>(aBackgroundActor)->mIsOtherProcessActor;
+}
+
+// static
+already_AddRefed<ContentParent>
+ParentImpl::GetContentParent(PBackgroundParent* aBackgroundActor)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+
+ auto actor = static_cast<ParentImpl*>(aBackgroundActor);
+ if (actor->mActorDestroyed) {
+ MOZ_ASSERT(false, "GetContentParent called after ActorDestroy was called!");
+ return nullptr;
+ }
+
+ if (actor->mContent) {
+ // We need to hand out a reference to our ContentParent but we also need to
+ // keep the one we have. We can't call AddRef here because ContentParent is
+ // not threadsafe so instead we dispatch a runnable to the main thread to do
+ // it for us. This is safe since we are guaranteed that our AddRef runnable
+ // will run before the reference we hand out can be released, and the
+ // ContentParent can't die as long as the existing reference is maintained.
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor->mContent, &ContentParent::AddRef)));
+ }
+
+ return already_AddRefed<ContentParent>(actor->mContent.get());
+}
+
+// static
+intptr_t
+ParentImpl::GetRawContentParentForComparison(
+ PBackgroundParent* aBackgroundActor)
+{
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aBackgroundActor);
+
+ auto actor = static_cast<ParentImpl*>(aBackgroundActor);
+ if (actor->mActorDestroyed) {
+ MOZ_ASSERT(false,
+ "GetRawContentParentForComparison called after ActorDestroy was "
+ "called!");
+ return intptr_t(-1);
+ }
+
+ return intptr_t(static_cast<nsIContentParent*>(actor->mContent.get()));
+}
+
+// static
+PBackgroundParent*
+ParentImpl::Alloc(ContentParent* aContent,
+ Transport* aTransport,
+ ProcessId aOtherPid)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aTransport);
+
+ if (!sBackgroundThread && !CreateBackgroundThread()) {
+ NS_WARNING("Failed to create background thread!");
+ return nullptr;
+ }
+
+ MOZ_ASSERT(sLiveActorsForBackgroundThread);
+
+ sLiveActorCount++;
+
+ RefPtr<ParentImpl> actor = new ParentImpl(aContent);
+
+ nsCOMPtr<nsIRunnable> connectRunnable =
+ new ConnectActorRunnable(actor, aTransport, aOtherPid,
+ sLiveActorsForBackgroundThread);
+
+ if (NS_FAILED(sBackgroundThread->Dispatch(connectRunnable,
+ NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch connect runnable!");
+
+ MOZ_ASSERT(sLiveActorCount);
+ sLiveActorCount--;
+
+ return nullptr;
+ }
+
+ return actor;
+}
+
+// static
+bool
+ParentImpl::CreateActorForSameProcess(CreateCallback* aCallback)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aCallback);
+
+ if (!sBackgroundThread && !CreateBackgroundThread()) {
+ NS_WARNING("Failed to create background thread!");
+ return false;
+ }
+
+ MOZ_ASSERT(!sShutdownHasStarted);
+
+ sLiveActorCount++;
+
+ if (sBackgroundThreadMessageLoop) {
+ nsCOMPtr<nsIRunnable> callbackRunnable =
+ new CreateCallbackRunnable(aCallback);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(callbackRunnable));
+ return true;
+ }
+
+ if (!sPendingCallbacks) {
+ sPendingCallbacks = new nsTArray<RefPtr<CreateCallback>>();
+ }
+
+ sPendingCallbacks->AppendElement(aCallback);
+ return true;
+}
+
+// static
+bool
+ParentImpl::CreateBackgroundThread()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!sBackgroundThread);
+ MOZ_ASSERT(!sLiveActorsForBackgroundThread);
+
+ if (sShutdownHasStarted) {
+ NS_WARNING("Trying to create background thread after shutdown has "
+ "already begun!");
+ return false;
+ }
+
+ nsCOMPtr<nsITimer> newShutdownTimer;
+
+ if (!sShutdownTimer) {
+ nsresult rv;
+ newShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ }
+
+ if (!sShutdownObserverRegistered) {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (NS_WARN_IF(!obs)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
+
+ nsresult rv =
+ obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ sShutdownObserverRegistered = true;
+ }
+
+ nsCOMPtr<nsIThread> thread;
+ if (NS_FAILED(NS_NewNamedThread("IPDL Background", getter_AddRefs(thread)))) {
+ NS_WARNING("NS_NewNamedThread failed!");
+ return false;
+ }
+
+ nsCOMPtr<nsIRunnable> messageLoopRunnable =
+ new RequestMessageLoopRunnable(thread);
+ if (NS_FAILED(thread->Dispatch(messageLoopRunnable, NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch RequestMessageLoopRunnable!");
+ return false;
+ }
+
+ sBackgroundThread = thread;
+ sLiveActorsForBackgroundThread = new nsTArray<ParentImpl*>(1);
+
+ if (!sShutdownTimer) {
+ MOZ_ASSERT(newShutdownTimer);
+ sShutdownTimer = newShutdownTimer;
+ }
+
+ return true;
+}
+
+// static
+void
+ParentImpl::ShutdownBackgroundThread()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT_IF(!sBackgroundThread, !sBackgroundThreadMessageLoop);
+ MOZ_ASSERT(sShutdownHasStarted);
+ MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount);
+ MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer);
+
+ if (sPendingCallbacks) {
+ if (!sPendingCallbacks->IsEmpty()) {
+ nsTArray<RefPtr<CreateCallback>> callbacks;
+ sPendingCallbacks->SwapElements(callbacks);
+
+ for (uint32_t index = 0; index < callbacks.Length(); index++) {
+ RefPtr<CreateCallback> callback;
+ callbacks[index].swap(callback);
+ MOZ_ASSERT(callback);
+
+ callback->Failure();
+ }
+ }
+
+ sPendingCallbacks = nullptr;
+ }
+
+ nsCOMPtr<nsITimer> shutdownTimer = sShutdownTimer.get();
+ sShutdownTimer = nullptr;
+
+ if (sBackgroundThread) {
+ nsCOMPtr<nsIThread> thread = sBackgroundThread.get();
+ sBackgroundThread = nullptr;
+
+ nsAutoPtr<nsTArray<ParentImpl*>> liveActors(sLiveActorsForBackgroundThread);
+ sLiveActorsForBackgroundThread = nullptr;
+
+ sBackgroundThreadMessageLoop = nullptr;
+
+ MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount);
+
+ if (sLiveActorCount) {
+ // We need to spin the event loop while we wait for all the actors to be
+ // cleaned up. We also set a timeout to force-kill any hanging actors.
+ TimerCallbackClosure closure(thread, liveActors);
+
+ MOZ_ALWAYS_SUCCEEDS(
+ shutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback,
+ &closure,
+ kShutdownTimerDelayMS,
+ nsITimer::TYPE_ONE_SHOT));
+
+ nsIThread* currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread);
+
+ while (sLiveActorCount) {
+ NS_ProcessNextEvent(currentThread);
+ }
+
+ MOZ_ASSERT(liveActors->IsEmpty());
+
+ MOZ_ALWAYS_SUCCEEDS(shutdownTimer->Cancel());
+ }
+
+ // Dispatch this runnable to unregister the thread from the profiler.
+ nsCOMPtr<nsIRunnable> shutdownRunnable =
+ new ShutdownBackgroundThreadRunnable();
+ MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
+
+ MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
+ }
+}
+
+// static
+void
+ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(sShutdownHasStarted);
+ MOZ_ASSERT(sLiveActorCount);
+
+ auto closure = static_cast<TimerCallbackClosure*>(aClosure);
+ MOZ_ASSERT(closure);
+
+ // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has
+ // finished.
+ sLiveActorCount++;
+
+ nsCOMPtr<nsIRunnable> forceCloseRunnable =
+ new ForceCloseBackgroundActorsRunnable(closure->mLiveActors);
+ MOZ_ALWAYS_SUCCEEDS(closure->mThread->Dispatch(forceCloseRunnable,
+ NS_DISPATCH_NORMAL));
+}
+
+void
+ParentImpl::Destroy()
+{
+ // May be called on any thread!
+
+ AssertIsInMainProcess();
+
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToMainThread(NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy)));
+}
+
+void
+ParentImpl::MainThreadActorDestroy()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT_IF(mIsOtherProcessActor, mContent);
+ MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent);
+
+ mContent = nullptr;
+
+ MOZ_ASSERT(sLiveActorCount);
+ sLiveActorCount--;
+
+ // This may be the last reference!
+ Release();
+}
+
+void
+ParentImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mActorDestroyed);
+ MOZ_ASSERT_IF(mIsOtherProcessActor, mLiveActorArray);
+
+ BackgroundParentImpl::ActorDestroy(aWhy);
+
+ mActorDestroyed = true;
+
+ if (mLiveActorArray) {
+ MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this));
+ mLiveActorArray = nullptr;
+ }
+
+ // This is tricky. We should be able to call Destroy() here directly because
+ // we're not going to touch 'this' or our MessageChannel any longer on this
+ // thread. Destroy() dispatches the MainThreadActorDestroy runnable and when
+ // it runs it will destroy 'this' and our associated MessageChannel. However,
+ // IPDL is about to call MessageChannel::Clear() on this thread! To avoid
+ // racing with the main thread we must ensure that the MessageChannel lives
+ // long enough to be cleared in this call stack.
+
+ MOZ_ALWAYS_SUCCEEDS(
+ NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(this, &ParentImpl::Destroy)));
+}
+
+NS_IMPL_ISUPPORTS(ParentImpl::ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!sShutdownHasStarted);
+ MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
+
+ sShutdownHasStarted = true;
+
+ // Do this first before calling (and spinning the event loop in)
+ // ShutdownBackgroundThread().
+ ChildImpl::Shutdown();
+
+ ShutdownBackgroundThread();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentImpl::RequestMessageLoopRunnable::Run()
+{
+ AssertIsInMainProcess();
+ MOZ_ASSERT(mTargetThread);
+
+ char stackBaseGuess;
+
+ if (NS_IsMainThread()) {
+ MOZ_ASSERT(mMessageLoop);
+
+ if (!sBackgroundThread ||
+ !SameCOMIdentity(mTargetThread.get(), sBackgroundThread.get())) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(!sBackgroundThreadMessageLoop);
+ sBackgroundThreadMessageLoop = mMessageLoop;
+
+ if (sPendingCallbacks && !sPendingCallbacks->IsEmpty()) {
+ nsTArray<RefPtr<CreateCallback>> callbacks;
+ sPendingCallbacks->SwapElements(callbacks);
+
+ for (uint32_t index = 0; index < callbacks.Length(); index++) {
+ MOZ_ASSERT(callbacks[index]);
+
+ nsCOMPtr<nsIRunnable> callbackRunnable =
+ new CreateCallbackRunnable(callbacks[index]);
+ if (NS_FAILED(NS_DispatchToCurrentThread(callbackRunnable))) {
+ NS_WARNING("Failed to dispatch callback runnable!");
+ }
+ }
+ }
+
+ return NS_OK;
+ }
+
+ profiler_register_thread("IPDL Background", &stackBaseGuess);
+
+#ifdef DEBUG
+ {
+ bool correctThread;
+ MOZ_ASSERT(NS_SUCCEEDED(mTargetThread->IsOnCurrentThread(&correctThread)));
+ MOZ_ASSERT(correctThread);
+ }
+#endif
+
+ DebugOnly<PRThread*> oldBackgroundThread =
+ sBackgroundPRThread.exchange(PR_GetCurrentThread());
+
+ MOZ_ASSERT_IF(oldBackgroundThread,
+ PR_GetCurrentThread() != oldBackgroundThread);
+
+ MOZ_ASSERT(!mMessageLoop);
+
+ mMessageLoop = MessageLoop::current();
+ MOZ_ASSERT(mMessageLoop);
+
+ if (NS_FAILED(NS_DispatchToMainThread(this))) {
+ NS_WARNING("Failed to dispatch RequestMessageLoopRunnable to main thread!");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentImpl::ShutdownBackgroundThreadRunnable::Run()
+{
+ AssertIsInMainProcess();
+
+ // It is possible that another background thread was created while this thread
+ // was shutting down. In that case we can't assert anything about
+ // sBackgroundPRThread and we should not modify it here.
+ sBackgroundPRThread.compareExchange(PR_GetCurrentThread(), nullptr);
+
+ profiler_unregister_thread();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentImpl::ForceCloseBackgroundActorsRunnable::Run()
+{
+ AssertIsInMainProcess();
+ MOZ_ASSERT(mActorArray);
+
+ if (NS_IsMainThread()) {
+ MOZ_ASSERT(sLiveActorCount);
+ sLiveActorCount--;
+ return NS_OK;
+ }
+
+ AssertIsOnBackgroundThread();
+
+ if (!mActorArray->IsEmpty()) {
+ // Copy the array since calling Close() could mutate the actual array.
+ nsTArray<ParentImpl*> actorsToClose(*mActorArray);
+
+ for (uint32_t index = 0; index < actorsToClose.Length(); index++) {
+ actorsToClose[index]->Close();
+ }
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentImpl::CreateCallbackRunnable::Run()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(sBackgroundThreadMessageLoop);
+ MOZ_ASSERT(mCallback);
+
+ RefPtr<CreateCallback> callback;
+ mCallback.swap(callback);
+
+ RefPtr<ParentImpl> actor = new ParentImpl();
+
+ callback->Success(actor.forget(), sBackgroundThreadMessageLoop);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentImpl::ConnectActorRunnable::Run()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ // Transfer ownership to this thread. If Open() fails then we will release
+ // this reference in Destroy.
+ ParentImpl* actor;
+ mActor.forget(&actor);
+
+ if (!actor->Open(mTransport, mOtherPid, XRE_GetIOMessageLoop(), ParentSide)) {
+ actor->Destroy();
+ return NS_ERROR_FAILURE;
+ }
+
+ actor->SetLiveActorArray(mLiveActorArray);
+
+ return NS_OK;
+}
+
+// -----------------------------------------------------------------------------
+// ChildImpl Implementation
+// -----------------------------------------------------------------------------
+
+// static
+void
+ChildImpl::Startup()
+{
+ // This happens on the main thread but before XPCOM has started so we can't
+ // assert that we're being called on the main thread here.
+
+ MOZ_ASSERT(sThreadLocalIndex == kBadThreadLocalIndex,
+ "BackgroundChild::Startup() called more than once!");
+
+ PRStatus status =
+ PR_NewThreadPrivateIndex(&sThreadLocalIndex, ThreadLocalDestructor);
+ MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!");
+
+ MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+
+ nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+
+ nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
+
+ nsresult rv =
+ observerService->AddObserver(observer,
+ NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
+ false);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+}
+
+// static
+void
+ChildImpl::Shutdown()
+{
+ AssertIsOnMainThread();
+
+ if (sShutdownHasStarted) {
+ MOZ_ASSERT_IF(sThreadLocalIndex != kBadThreadLocalIndex,
+ !PR_GetThreadPrivate(sThreadLocalIndex));
+ return;
+ }
+
+ sShutdownHasStarted = true;
+
+#ifdef DEBUG
+ MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (threadLocalInfo) {
+ MOZ_ASSERT(!threadLocalInfo->mClosed);
+ threadLocalInfo->mClosed = true;
+ }
+#endif
+
+ DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
+ MOZ_ASSERT(status == PR_SUCCESS);
+}
+
+// static
+PBackgroundChild*
+ChildImpl::Alloc(Transport* aTransport, ProcessId aOtherPid)
+{
+ AssertIsInChildProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aTransport);
+ MOZ_ASSERT(sPendingTargets);
+ MOZ_ASSERT(!sPendingTargets->IsEmpty());
+
+ nsCOMPtr<nsIEventTarget> eventTarget;
+ sPendingTargets->ElementAt(0).swap(eventTarget);
+
+ sPendingTargets->RemoveElementAt(0);
+
+ RefPtr<ChildImpl> actor = new ChildImpl();
+
+ ChildImpl* weakActor = actor;
+
+ nsCOMPtr<nsIRunnable> openRunnable =
+ new OpenChildProcessActorRunnable(actor.forget(), aTransport,
+ aOtherPid);
+ if (NS_FAILED(eventTarget->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
+ MOZ_CRASH("Failed to dispatch OpenActorRunnable!");
+ }
+
+ // This value is only checked against null to determine success/failure, so
+ // there is no need to worry about the reference count here.
+ return weakActor;
+}
+
+// static
+PBackgroundChild*
+ChildImpl::GetForCurrentThread()
+{
+ MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (!threadLocalInfo) {
+ return nullptr;
+ }
+
+ return threadLocalInfo->mActor;
+}
+
+// static
+bool
+ChildImpl::GetOrCreateForCurrentThread(
+ nsIIPCBackgroundChildCreateCallback* aCallback)
+{
+ MOZ_ASSERT(aCallback);
+ MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
+ "BackgroundChild::Startup() was never called!");
+
+ bool created = false;
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (threadLocalInfo) {
+ threadLocalInfo->mCallbacks.AppendElement(aCallback);
+ } else {
+ nsAutoPtr<ThreadLocalInfo> newInfo(new ThreadLocalInfo(aCallback));
+
+ if (PR_SetThreadPrivate(sThreadLocalIndex, newInfo) != PR_SUCCESS) {
+ CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
+ return false;
+ }
+
+ created = true;
+ threadLocalInfo = newInfo.forget();
+ }
+
+ if (threadLocalInfo->mActor) {
+ // Runnable will use GetForCurrentThread() to retrieve actor again. This
+ // allows us to avoid addref'ing on the wrong thread.
+ nsCOMPtr<nsIRunnable> runnable = new AlreadyCreatedCallbackRunnable();
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
+
+ return true;
+ }
+
+ if (!created) {
+ // We have already started the sequence for opening the actor so there's
+ // nothing else we need to do here. This callback will be called after the
+ // first callback in the schedule runnable.
+ return true;
+ }
+
+ if (NS_IsMainThread()) {
+ if (NS_WARN_IF(!OpenProtocolOnMainThread(NS_GetCurrentThread()))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ RefPtr<CreateActorRunnable> runnable = new CreateActorRunnable();
+ if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+ CRASH_IN_CHILD_PROCESS("Failed to dispatch to main thread!");
+ return false;
+ }
+
+ return true;
+}
+
+namespace {
+
+class Callback final : public nsIIPCBackgroundChildCreateCallback
+{
+ bool* mDone;
+
+public:
+ explicit Callback(bool* aDone)
+ : mDone(aDone)
+ {
+ MOZ_ASSERT(mDone);
+ }
+
+ NS_DECL_ISUPPORTS
+
+private:
+ ~Callback()
+ { }
+
+ virtual void
+ ActorCreated(PBackgroundChild* aActor) override
+ {
+ *mDone = true;
+ }
+
+ virtual void
+ ActorFailed() override
+ {
+ *mDone = true;
+ }
+};
+
+NS_IMPL_ISUPPORTS(Callback, nsIIPCBackgroundChildCreateCallback)
+
+} // anonymous namespace
+
+/* static */
+PBackgroundChild*
+ChildImpl::SynchronouslyCreateForCurrentThread()
+{
+ MOZ_ASSERT(!GetForCurrentThread());
+
+ bool done = false;
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = new Callback(&done);
+
+ if (NS_WARN_IF(!GetOrCreateForCurrentThread(callback))) {
+ return nullptr;
+ }
+
+ nsIThread* currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread);
+
+ while (!done) {
+ if (NS_WARN_IF(!NS_ProcessNextEvent(currentThread, true /* aMayWait */))) {
+ return nullptr;
+ }
+ }
+
+ return GetForCurrentThread();
+}
+
+// static
+void
+ChildImpl::CloseForCurrentThread()
+{
+ if (sThreadLocalIndex == kBadThreadLocalIndex) {
+ return;
+ }
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (!threadLocalInfo) {
+ return;
+ }
+
+#ifdef DEBUG
+ MOZ_ASSERT(!threadLocalInfo->mClosed);
+ threadLocalInfo->mClosed = true;
+#endif
+
+ // Clearing the thread local will synchronously close the actor.
+ DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
+ MOZ_ASSERT(status == PR_SUCCESS);
+}
+
+// static
+BackgroundChildImpl::ThreadLocal*
+ChildImpl::GetThreadLocalForCurrentThread()
+{
+ MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
+ "BackgroundChild::Startup() was never called!");
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ if (!threadLocalInfo) {
+ return nullptr;
+ }
+
+ if (!threadLocalInfo->mConsumerThreadLocal) {
+ threadLocalInfo->mConsumerThreadLocal =
+ new BackgroundChildImpl::ThreadLocal();
+ }
+
+ return threadLocalInfo->mConsumerThreadLocal;
+}
+
+// static
+already_AddRefed<nsIIPCBackgroundChildCreateCallback>
+ChildImpl::GetNextCallback()
+{
+ // May run on any thread!
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+ MOZ_ASSERT(threadLocalInfo);
+
+ if (threadLocalInfo->mCallbacks.IsEmpty()) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback;
+ threadLocalInfo->mCallbacks[0].swap(callback);
+
+ threadLocalInfo->mCallbacks.RemoveElementAt(0);
+
+ return callback.forget();
+}
+
+NS_IMETHODIMP
+ChildImpl::AlreadyCreatedCallbackRunnable::Run()
+{
+ // May run on any thread!
+
+ // Report the current actor back in the callback.
+ PBackgroundChild* actor = ChildImpl::GetForCurrentThread();
+
+ // If the current actor is null, do not create a new actor here. This likely
+ // means we are in the process of cleaning up a worker thread and do not want
+ // a new actor created. Unfortunately we cannot report back to the callback
+ // because the thread local is gone at this point. Instead simply do nothing
+ // and return.
+ if (NS_WARN_IF(!actor)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ while (callback) {
+ callback->ActorCreated(actor);
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+ChildImpl::AlreadyCreatedCallbackRunnable::Cancel()
+{
+ // These are IPC infrastructure objects and need to run unconditionally.
+ Run();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildImpl::FailedCreateCallbackRunnable::Run()
+{
+ // May run on any thread!
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ while (callback) {
+ callback->ActorFailed();
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildImpl::OpenChildProcessActorRunnable::Run()
+{
+ // May be run on any thread!
+
+ AssertIsInChildProcess();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mTransport);
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ MOZ_ASSERT(callback,
+ "There should be at least one callback when first creating the "
+ "actor!");
+
+ RefPtr<ChildImpl> strongActor;
+ mActor.swap(strongActor);
+
+ if (!strongActor->Open(mTransport.forget(), mOtherPid,
+ XRE_GetIOMessageLoop(), ChildSide)) {
+ CRASH_IN_CHILD_PROCESS("Failed to open ChildImpl!");
+
+ while (callback) {
+ callback->ActorFailed();
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+ }
+
+ // Now that Open() has succeeded transfer the ownership of the actor to IPDL.
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ MOZ_ASSERT(threadLocalInfo);
+ MOZ_ASSERT(!threadLocalInfo->mActor);
+
+ RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
+ strongActor.swap(actor);
+
+ actor->SetBoundThread();
+
+ while (callback) {
+ callback->ActorCreated(actor);
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildImpl::OpenMainProcessActorRunnable::Run()
+{
+ // May run on any thread!
+
+ AssertIsInMainProcess();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mParentActor);
+ MOZ_ASSERT(mParentMessageLoop);
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ ChildImpl::GetNextCallback();
+ MOZ_ASSERT(callback,
+ "There should be at least one callback when first creating the "
+ "actor!");
+
+ RefPtr<ChildImpl> strongChildActor;
+ mActor.swap(strongChildActor);
+
+ RefPtr<ParentImpl> parentActor;
+ mParentActor.swap(parentActor);
+
+ MessageChannel* parentChannel = parentActor->GetIPCChannel();
+ MOZ_ASSERT(parentChannel);
+
+ if (!strongChildActor->Open(parentChannel, mParentMessageLoop, ChildSide)) {
+ NS_WARNING("Failed to open ChildImpl!");
+
+ parentActor->Destroy();
+
+ while (callback) {
+ callback->ActorFailed();
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+ }
+
+ // Make sure the parent knows it is same process.
+ parentActor->SetOtherProcessId(base::GetCurrentProcId());
+
+ // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
+ Unused << parentActor.forget();
+
+ auto threadLocalInfo =
+ static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
+
+ MOZ_ASSERT(threadLocalInfo);
+ MOZ_ASSERT(!threadLocalInfo->mActor);
+
+ RefPtr<ChildImpl>& childActor = threadLocalInfo->mActor;
+ strongChildActor.swap(childActor);
+
+ childActor->SetBoundThread();
+
+ while (callback) {
+ callback->ActorCreated(childActor);
+ callback = ChildImpl::GetNextCallback();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildImpl::CreateActorRunnable::Run()
+{
+ AssertIsOnMainThread();
+
+ if (!OpenProtocolOnMainThread(mEventTarget)) {
+ NS_WARNING("OpenProtocolOnMainThread failed!");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+void
+ChildImpl::ParentCreateCallback::Success(
+ already_AddRefed<ParentImpl> aParentActor,
+ MessageLoop* aParentMessageLoop)
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+
+ RefPtr<ParentImpl> parentActor = aParentActor;
+ MOZ_ASSERT(parentActor);
+ MOZ_ASSERT(aParentMessageLoop);
+ MOZ_ASSERT(mEventTarget);
+
+ RefPtr<ChildImpl> childActor = new ChildImpl();
+
+ nsCOMPtr<nsIEventTarget> target;
+ mEventTarget.swap(target);
+
+ nsCOMPtr<nsIRunnable> openRunnable =
+ new OpenMainProcessActorRunnable(childActor.forget(), parentActor.forget(),
+ aParentMessageLoop);
+ if (NS_FAILED(target->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch open runnable!");
+ }
+}
+
+void
+ChildImpl::ParentCreateCallback::Failure()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mEventTarget);
+
+ nsCOMPtr<nsIEventTarget> target;
+ mEventTarget.swap(target);
+
+ DispatchFailureCallback(target);
+}
+
+// static
+bool
+ChildImpl::OpenProtocolOnMainThread(nsIEventTarget* aEventTarget)
+{
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aEventTarget);
+
+ if (sShutdownHasStarted) {
+ MOZ_CRASH("Called BackgroundChild::GetOrCreateForCurrentThread after "
+ "shutdown has started!");
+ }
+
+ if (XRE_IsParentProcess()) {
+ RefPtr<ParentImpl::CreateCallback> parentCallback =
+ new ParentCreateCallback(aEventTarget);
+
+ if (!ParentImpl::CreateActorForSameProcess(parentCallback)) {
+ NS_WARNING("BackgroundParent::CreateActor() failed!");
+ DispatchFailureCallback(aEventTarget);
+ return false;
+ }
+
+ return true;
+ }
+
+ ContentChild* content = ContentChild::GetSingleton();
+ MOZ_ASSERT(content);
+
+ if (content->IsShuttingDown()) {
+ // The transport for ContentChild is shut down and can't be used to open
+ // PBackground.
+ DispatchFailureCallback(aEventTarget);
+ return false;
+ }
+
+ if (!PBackground::Open(content)) {
+ MOZ_CRASH("Failed to create top level actor!");
+ return false;
+ }
+
+ if (!sPendingTargets) {
+ sPendingTargets = new nsTArray<nsCOMPtr<nsIEventTarget>>(1);
+ ClearOnShutdown(&sPendingTargets);
+ }
+
+ sPendingTargets->AppendElement(aEventTarget);
+
+ return true;
+}
+
+// static
+void
+ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget)
+{
+ MOZ_ASSERT(aEventTarget);
+
+ nsCOMPtr<nsIRunnable> callbackRunnable = new FailedCreateCallbackRunnable();
+ if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) {
+ NS_WARNING("Failed to dispatch CreateCallbackRunnable!");
+ }
+}
+
+void
+ChildImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnBoundThread();
+
+#ifdef DEBUG
+ MOZ_ASSERT(!mActorDestroyed);
+ mActorDestroyed = true;
+#endif
+
+ BackgroundChildImpl::ActorDestroy(aWhy);
+}
+
+NS_IMPL_ISUPPORTS(ChildImpl::ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));
+
+ ChildImpl::Shutdown();
+
+ return NS_OK;
+}
diff --git a/ipc/glue/BackgroundParent.h b/ipc/glue/BackgroundParent.h
new file mode 100644
index 000000000..64530af99
--- /dev/null
+++ b/ipc/glue/BackgroundParent.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_backgroundparent_h__
+#define mozilla_ipc_backgroundparent_h__
+
+#include "base/process.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/Transport.h"
+
+template <class> struct already_AddRefed;
+
+namespace mozilla {
+namespace dom {
+
+class BlobImpl;
+class ContentParent;
+class PBlobParent;
+
+} // namespace dom
+
+namespace ipc {
+
+class PBackgroundParent;
+
+// This class is not designed for public consumption beyond the few static
+// member functions.
+class BackgroundParent final
+{
+ friend class mozilla::dom::ContentParent;
+
+ typedef base::ProcessId ProcessId;
+ typedef mozilla::dom::BlobImpl BlobImpl;
+ typedef mozilla::dom::ContentParent ContentParent;
+ typedef mozilla::ipc::Transport Transport;
+
+public:
+ // This function allows the caller to determine if the given parent actor
+ // corresponds to a child actor from another process or a child actor from a
+ // different thread in the same process.
+ // This function may only be called on the background thread.
+ static bool
+ IsOtherProcessActor(PBackgroundParent* aBackgroundActor);
+
+ // This function returns the ContentParent associated with the parent actor if
+ // the parent actor corresponds to a child actor from another process. If the
+ // parent actor corresponds to a child actor from a different thread in the
+ // same process then this function returns null.
+ // This function may only be called on the background thread. However,
+ // ContentParent is not threadsafe and the returned pointer may not be used on
+ // any thread other than the main thread. Callers must take care to use (and
+ // release) the returned pointer appropriately.
+ static already_AddRefed<ContentParent>
+ GetContentParent(PBackgroundParent* aBackgroundActor);
+
+ static mozilla::dom::PBlobParent*
+ GetOrCreateActorForBlobImpl(PBackgroundParent* aBackgroundActor,
+ BlobImpl* aBlobImpl);
+
+ // Get a value that represents the ContentParent associated with the parent
+ // actor for comparison. The value is not guaranteed to uniquely identify the
+ // ContentParent after the ContentParent has died. This function may only be
+ // called on the background thread.
+ static intptr_t
+ GetRawContentParentForComparison(PBackgroundParent* aBackgroundActor);
+
+private:
+ // Only called by ContentParent for cross-process actors.
+ static PBackgroundParent*
+ Alloc(ContentParent* aContent,
+ Transport* aTransport,
+ ProcessId aOtherProcess);
+};
+
+// Implemented in BackgroundImpl.cpp.
+bool
+IsOnBackgroundThread();
+
+#ifdef DEBUG
+
+// Implemented in BackgroundImpl.cpp.
+void
+AssertIsOnBackgroundThread();
+
+#else
+
+inline void
+AssertIsOnBackgroundThread()
+{ }
+
+#endif // DEBUG
+
+inline void
+AssertIsInMainProcess()
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+}
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundparent_h__
diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp
new file mode 100644
index 000000000..ef5dc1cab
--- /dev/null
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -0,0 +1,848 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "BackgroundParentImpl.h"
+
+#include "BroadcastChannelParent.h"
+#include "FileDescriptorSetParent.h"
+#ifdef MOZ_WEBRTC
+#include "CamerasParent.h"
+#endif
+#include "mozilla/media/MediaParent.h"
+#include "mozilla/AppProcessChecker.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/dom/FileSystemBase.h"
+#include "mozilla/dom/FileSystemRequestParent.h"
+#ifdef MOZ_GAMEPAD
+#include "mozilla/dom/GamepadEventChannelParent.h"
+#include "mozilla/dom/GamepadTestChannelParent.h"
+#endif
+#include "mozilla/dom/PBlobParent.h"
+#include "mozilla/dom/PGamepadEventChannelParent.h"
+#include "mozilla/dom/PGamepadTestChannelParent.h"
+#include "mozilla/dom/MessagePortParent.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/asmjscache/AsmJSCache.h"
+#include "mozilla/dom/cache/ActorUtils.h"
+#include "mozilla/dom/indexedDB/ActorsParent.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/quota/ActorsParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "mozilla/ipc/PBackgroundTestParent.h"
+#include "mozilla/ipc/PSendStreamParent.h"
+#include "mozilla/ipc/SendStreamAlloc.h"
+#include "mozilla/layout/VsyncParent.h"
+#include "mozilla/dom/network/UDPSocketParent.h"
+#include "mozilla/Preferences.h"
+#include "nsIAppsService.h"
+#include "nsNetUtil.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsProxyRelease.h"
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+#include "nsTraceRefcnt.h"
+#include "nsXULAppAPI.h"
+#include "ServiceWorkerManagerParent.h"
+
+#ifdef DISABLE_ASSERTS_FOR_FUZZING
+#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
+#else
+#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false)
+#endif
+
+using mozilla::ipc::AssertIsOnBackgroundThread;
+using mozilla::dom::asmjscache::PAsmJSCacheEntryParent;
+using mozilla::dom::cache::PCacheParent;
+using mozilla::dom::cache::PCacheStorageParent;
+using mozilla::dom::cache::PCacheStreamControlParent;
+using mozilla::dom::FileSystemBase;
+using mozilla::dom::FileSystemRequestParent;
+using mozilla::dom::MessagePortParent;
+using mozilla::dom::PMessagePortParent;
+using mozilla::dom::UDPSocketParent;
+
+namespace {
+
+void
+AssertIsOnMainThread()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+class TestParent final : public mozilla::ipc::PBackgroundTestParent
+{
+ friend class mozilla::ipc::BackgroundParentImpl;
+
+ TestParent()
+ {
+ MOZ_COUNT_CTOR(TestParent);
+ }
+
+protected:
+ ~TestParent()
+ {
+ MOZ_COUNT_DTOR(TestParent);
+ }
+
+public:
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+
+using mozilla::dom::ContentParent;
+using mozilla::dom::BroadcastChannelParent;
+using mozilla::dom::ServiceWorkerRegistrationData;
+using mozilla::dom::workers::ServiceWorkerManagerParent;
+
+BackgroundParentImpl::BackgroundParentImpl()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+
+ MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl);
+}
+
+BackgroundParentImpl::~BackgroundParentImpl()
+{
+ AssertIsInMainProcess();
+ AssertIsOnMainThread();
+
+ MOZ_COUNT_DTOR(mozilla::ipc::BackgroundParentImpl);
+}
+
+void
+BackgroundParentImpl::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+}
+
+BackgroundParentImpl::PBackgroundTestParent*
+BackgroundParentImpl::AllocPBackgroundTestParent(const nsCString& aTestArg)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return new TestParent();
+}
+
+bool
+BackgroundParentImpl::RecvPBackgroundTestConstructor(
+ PBackgroundTestParent* aActor,
+ const nsCString& aTestArg)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ return PBackgroundTestParent::Send__delete__(aActor, aTestArg);
+}
+
+bool
+BackgroundParentImpl::DeallocPBackgroundTestParent(
+ PBackgroundTestParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<TestParent*>(aActor);
+ return true;
+}
+
+auto
+BackgroundParentImpl::AllocPBackgroundIDBFactoryParent(
+ const LoggingInfo& aLoggingInfo)
+ -> PBackgroundIDBFactoryParent*
+{
+ using mozilla::dom::indexedDB::AllocPBackgroundIDBFactoryParent;
+
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return AllocPBackgroundIDBFactoryParent(aLoggingInfo);
+}
+
+bool
+BackgroundParentImpl::RecvPBackgroundIDBFactoryConstructor(
+ PBackgroundIDBFactoryParent* aActor,
+ const LoggingInfo& aLoggingInfo)
+{
+ using mozilla::dom::indexedDB::RecvPBackgroundIDBFactoryConstructor;
+
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ return RecvPBackgroundIDBFactoryConstructor(aActor, aLoggingInfo);
+}
+
+bool
+BackgroundParentImpl::DeallocPBackgroundIDBFactoryParent(
+ PBackgroundIDBFactoryParent* aActor)
+{
+ using mozilla::dom::indexedDB::DeallocPBackgroundIDBFactoryParent;
+
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ return DeallocPBackgroundIDBFactoryParent(aActor);
+}
+
+auto
+BackgroundParentImpl::AllocPBackgroundIndexedDBUtilsParent()
+ -> PBackgroundIndexedDBUtilsParent*
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return mozilla::dom::indexedDB::AllocPBackgroundIndexedDBUtilsParent();
+}
+
+bool
+BackgroundParentImpl::DeallocPBackgroundIndexedDBUtilsParent(
+ PBackgroundIndexedDBUtilsParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ return
+ mozilla::dom::indexedDB::DeallocPBackgroundIndexedDBUtilsParent(aActor);
+}
+
+bool
+BackgroundParentImpl::RecvFlushPendingFileDeletions()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return mozilla::dom::indexedDB::RecvFlushPendingFileDeletions();
+}
+
+auto
+BackgroundParentImpl::AllocPBlobParent(const BlobConstructorParams& aParams)
+ -> PBlobParent*
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ if (NS_WARN_IF(aParams.type() !=
+ BlobConstructorParams::TParentBlobConstructorParams)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ return mozilla::dom::BlobParent::Create(this, aParams);
+}
+
+bool
+BackgroundParentImpl::DeallocPBlobParent(PBlobParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ mozilla::dom::BlobParent::Destroy(aActor);
+ return true;
+}
+
+bool
+BackgroundParentImpl::RecvPBlobConstructor(PBlobParent* aActor,
+ const BlobConstructorParams& aParams)
+{
+ const ParentBlobConstructorParams& params = aParams;
+ if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) {
+ return aActor->SendCreatedFromKnownBlob();
+ }
+
+ return true;
+}
+
+PFileDescriptorSetParent*
+BackgroundParentImpl::AllocPFileDescriptorSetParent(
+ const FileDescriptor& aFileDescriptor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return new FileDescriptorSetParent(aFileDescriptor);
+}
+
+bool
+BackgroundParentImpl::DeallocPFileDescriptorSetParent(
+ PFileDescriptorSetParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<FileDescriptorSetParent*>(aActor);
+ return true;
+}
+
+PSendStreamParent*
+BackgroundParentImpl::AllocPSendStreamParent()
+{
+ return mozilla::ipc::AllocPSendStreamParent();
+}
+
+bool
+BackgroundParentImpl::DeallocPSendStreamParent(PSendStreamParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+BackgroundParentImpl::PVsyncParent*
+BackgroundParentImpl::AllocPVsyncParent()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ RefPtr<mozilla::layout::VsyncParent> actor =
+ mozilla::layout::VsyncParent::Create();
+ // There still has one ref-count after return, and it will be released in
+ // DeallocPVsyncParent().
+ return actor.forget().take();
+}
+
+bool
+BackgroundParentImpl::DeallocPVsyncParent(PVsyncParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ // This actor already has one ref-count. Please check AllocPVsyncParent().
+ RefPtr<mozilla::layout::VsyncParent> actor =
+ dont_AddRef(static_cast<mozilla::layout::VsyncParent*>(aActor));
+ return true;
+}
+
+camera::PCamerasParent*
+BackgroundParentImpl::AllocPCamerasParent()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+#ifdef MOZ_WEBRTC
+ RefPtr<mozilla::camera::CamerasParent> actor =
+ mozilla::camera::CamerasParent::Create();
+ return actor.forget().take();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+BackgroundParentImpl::DeallocPCamerasParent(camera::PCamerasParent *aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+#ifdef MOZ_WEBRTC
+ RefPtr<mozilla::camera::CamerasParent> actor =
+ dont_AddRef(static_cast<mozilla::camera::CamerasParent*>(aActor));
+#endif
+ return true;
+}
+
+namespace {
+
+class InitUDPSocketParentCallback final : public Runnable
+{
+public:
+ InitUDPSocketParentCallback(UDPSocketParent* aActor,
+ const nsACString& aFilter)
+ : mActor(aActor)
+ , mFilter(aFilter)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ AssertIsInMainProcess();
+
+ IPC::Principal principal;
+ if (!mActor->Init(principal, mFilter)) {
+ MOZ_CRASH("UDPSocketCallback - failed init");
+ }
+ return NS_OK;
+ }
+
+private:
+ ~InitUDPSocketParentCallback() {};
+
+ RefPtr<UDPSocketParent> mActor;
+ nsCString mFilter;
+};
+
+} // namespace
+
+auto
+BackgroundParentImpl::AllocPUDPSocketParent(const OptionalPrincipalInfo& /* unused */,
+ const nsCString& /* unused */)
+ -> PUDPSocketParent*
+{
+ RefPtr<UDPSocketParent> p = new UDPSocketParent(this);
+
+ return p.forget().take();
+}
+
+bool
+BackgroundParentImpl::RecvPUDPSocketConstructor(PUDPSocketParent* aActor,
+ const OptionalPrincipalInfo& aOptionalPrincipal,
+ const nsCString& aFilter)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ if (aOptionalPrincipal.type() == OptionalPrincipalInfo::TPrincipalInfo) {
+ // Support for checking principals (for non-mtransport use) will be handled in
+ // bug 1167039
+ return false;
+ }
+ // No principal - This must be from mtransport (WebRTC/ICE) - We'd want
+ // to DispatchToMainThread() here, but if we do we must block RecvBind()
+ // until Init() gets run. Since we don't have a principal, and we verify
+ // we have a filter, we can safely skip the Dispatch and just invoke Init()
+ // to install the filter.
+
+ // For mtransport, this will always be "stun", which doesn't allow outbound
+ // packets if they aren't STUN packets until a STUN response is seen.
+ if (!aFilter.EqualsASCII(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX)) {
+ return false;
+ }
+
+ IPC::Principal principal;
+ if (!static_cast<UDPSocketParent*>(aActor)->Init(principal, aFilter)) {
+ MOZ_CRASH("UDPSocketCallback - failed init");
+ }
+
+ return true;
+}
+
+bool
+BackgroundParentImpl::DeallocPUDPSocketParent(PUDPSocketParent* actor)
+{
+ UDPSocketParent* p = static_cast<UDPSocketParent*>(actor);
+ p->Release();
+ return true;
+}
+
+mozilla::dom::PBroadcastChannelParent*
+BackgroundParentImpl::AllocPBroadcastChannelParent(
+ const PrincipalInfo& aPrincipalInfo,
+ const nsCString& aOrigin,
+ const nsString& aChannel)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ nsString originChannelKey;
+
+ // The format of originChannelKey is:
+ // <channelName>|<origin+OriginAttributes>
+
+ originChannelKey.Assign(aChannel);
+
+ originChannelKey.AppendLiteral("|");
+
+ originChannelKey.Append(NS_ConvertUTF8toUTF16(aOrigin));
+
+ return new BroadcastChannelParent(originChannelKey);
+}
+
+namespace {
+
+struct MOZ_STACK_CLASS NullifyContentParentRAII
+{
+ explicit NullifyContentParentRAII(RefPtr<ContentParent>& aContentParent)
+ : mContentParent(aContentParent)
+ {}
+
+ ~NullifyContentParentRAII()
+ {
+ mContentParent = nullptr;
+ }
+
+ RefPtr<ContentParent>& mContentParent;
+};
+
+class CheckPrincipalRunnable final : public Runnable
+{
+public:
+ CheckPrincipalRunnable(already_AddRefed<ContentParent> aParent,
+ const PrincipalInfo& aPrincipalInfo,
+ const nsCString& aOrigin)
+ : mContentParent(aParent)
+ , mPrincipalInfo(aPrincipalInfo)
+ , mOrigin(aOrigin)
+ {
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ MOZ_ASSERT(mContentParent);
+ }
+
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ NullifyContentParentRAII raii(mContentParent);
+
+ nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
+ AssertAppPrincipal(mContentParent, principal);
+
+ if (principal->GetIsNullPrincipal()) {
+ mContentParent->KillHard("BroadcastChannel killed: no null principal.");
+ return NS_OK;
+ }
+
+ nsAutoCString origin;
+ nsresult rv = principal->GetOrigin(origin);
+ if (NS_FAILED(rv)) {
+ mContentParent->KillHard("BroadcastChannel killed: principal::GetOrigin failed.");
+ return NS_OK;
+ }
+
+ if (NS_WARN_IF(!mOrigin.Equals(origin))) {
+ mContentParent->KillHard("BroadcastChannel killed: origins do not match.");
+ return NS_OK;
+ }
+
+ return NS_OK;
+ }
+
+private:
+ RefPtr<ContentParent> mContentParent;
+ PrincipalInfo mPrincipalInfo;
+ nsCString mOrigin;
+};
+
+} // namespace
+
+bool
+BackgroundParentImpl::RecvPBroadcastChannelConstructor(
+ PBroadcastChannelParent* actor,
+ const PrincipalInfo& aPrincipalInfo,
+ const nsCString& aOrigin,
+ const nsString& aChannel)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
+
+ // If the ContentParent is null we are dealing with a same-process actor.
+ if (!parent) {
+ MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::TNullPrincipalInfo);
+ return true;
+ }
+
+ RefPtr<CheckPrincipalRunnable> runnable =
+ new CheckPrincipalRunnable(parent.forget(), aPrincipalInfo, aOrigin);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+
+ return true;
+}
+
+bool
+BackgroundParentImpl::DeallocPBroadcastChannelParent(
+ PBroadcastChannelParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<BroadcastChannelParent*>(aActor);
+ return true;
+}
+
+mozilla::dom::PServiceWorkerManagerParent*
+BackgroundParentImpl::AllocPServiceWorkerManagerParent()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ RefPtr<dom::workers::ServiceWorkerManagerParent> agent =
+ new dom::workers::ServiceWorkerManagerParent();
+ return agent.forget().take();
+}
+
+bool
+BackgroundParentImpl::DeallocPServiceWorkerManagerParent(
+ PServiceWorkerManagerParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ RefPtr<dom::workers::ServiceWorkerManagerParent> parent =
+ dont_AddRef(static_cast<dom::workers::ServiceWorkerManagerParent*>(aActor));
+ MOZ_ASSERT(parent);
+ return true;
+}
+
+bool
+BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ if (BackgroundParent::IsOtherProcessActor(this)) {
+ return false;
+ }
+
+ RefPtr<dom::ServiceWorkerRegistrar> service =
+ dom::ServiceWorkerRegistrar::Get();
+ MOZ_ASSERT(service);
+
+ service->Shutdown();
+ return true;
+}
+
+PCacheStorageParent*
+BackgroundParentImpl::AllocPCacheStorageParent(const Namespace& aNamespace,
+ const PrincipalInfo& aPrincipalInfo)
+{
+ return dom::cache::AllocPCacheStorageParent(this, aNamespace, aPrincipalInfo);
+}
+
+bool
+BackgroundParentImpl::DeallocPCacheStorageParent(PCacheStorageParent* aActor)
+{
+ dom::cache::DeallocPCacheStorageParent(aActor);
+ return true;
+}
+
+PCacheParent*
+BackgroundParentImpl::AllocPCacheParent()
+{
+ MOZ_CRASH("CacheParent actor must be provided to PBackground manager");
+ return nullptr;
+}
+
+bool
+BackgroundParentImpl::DeallocPCacheParent(PCacheParent* aActor)
+{
+ dom::cache::DeallocPCacheParent(aActor);
+ return true;
+}
+
+PCacheStreamControlParent*
+BackgroundParentImpl::AllocPCacheStreamControlParent()
+{
+ MOZ_CRASH("CacheStreamControlParent actor must be provided to PBackground manager");
+ return nullptr;
+}
+
+bool
+BackgroundParentImpl::DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor)
+{
+ dom::cache::DeallocPCacheStreamControlParent(aActor);
+ return true;
+}
+
+PMessagePortParent*
+BackgroundParentImpl::AllocPMessagePortParent(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return new MessagePortParent(aUUID);
+}
+
+bool
+BackgroundParentImpl::RecvPMessagePortConstructor(PMessagePortParent* aActor,
+ const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ MessagePortParent* mp = static_cast<MessagePortParent*>(aActor);
+ return mp->Entangle(aDestinationUUID, aSequenceID);
+}
+
+bool
+BackgroundParentImpl::DeallocPMessagePortParent(PMessagePortParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ delete static_cast<MessagePortParent*>(aActor);
+ return true;
+}
+
+bool
+BackgroundParentImpl::RecvMessagePortForceClose(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return MessagePortParent::ForceClose(aUUID, aDestinationUUID, aSequenceID);
+}
+
+PAsmJSCacheEntryParent*
+BackgroundParentImpl::AllocPAsmJSCacheEntryParent(
+ const dom::asmjscache::OpenMode& aOpenMode,
+ const dom::asmjscache::WriteParams& aWriteParams,
+ const PrincipalInfo& aPrincipalInfo)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return
+ dom::asmjscache::AllocEntryParent(aOpenMode, aWriteParams, aPrincipalInfo);
+}
+
+bool
+BackgroundParentImpl::DeallocPAsmJSCacheEntryParent(
+ PAsmJSCacheEntryParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ dom::asmjscache::DeallocEntryParent(aActor);
+ return true;
+}
+
+BackgroundParentImpl::PQuotaParent*
+BackgroundParentImpl::AllocPQuotaParent()
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ return mozilla::dom::quota::AllocPQuotaParent();
+}
+
+bool
+BackgroundParentImpl::DeallocPQuotaParent(PQuotaParent* aActor)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aActor);
+
+ return mozilla::dom::quota::DeallocPQuotaParent(aActor);
+}
+
+dom::PFileSystemRequestParent*
+BackgroundParentImpl::AllocPFileSystemRequestParent(
+ const FileSystemParams& aParams)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ RefPtr<FileSystemRequestParent> result = new FileSystemRequestParent();
+
+ if (NS_WARN_IF(!result->Initialize(aParams))) {
+ return nullptr;
+ }
+
+ return result.forget().take();
+}
+
+bool
+BackgroundParentImpl::RecvPFileSystemRequestConstructor(
+ PFileSystemRequestParent* aActor,
+ const FileSystemParams& params)
+{
+ static_cast<FileSystemRequestParent*>(aActor)->Start();
+ return true;
+}
+
+bool
+BackgroundParentImpl::DeallocPFileSystemRequestParent(
+ PFileSystemRequestParent* aDoomed)
+{
+ AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+
+ RefPtr<FileSystemRequestParent> parent =
+ dont_AddRef(static_cast<FileSystemRequestParent*>(aDoomed));
+ return true;
+}
+
+// Gamepad API Background IPC
+dom::PGamepadEventChannelParent*
+BackgroundParentImpl::AllocPGamepadEventChannelParent()
+{
+#ifdef MOZ_GAMEPAD
+ RefPtr<dom::GamepadEventChannelParent> parent =
+ new dom::GamepadEventChannelParent();
+
+ return parent.forget().take();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+BackgroundParentImpl::DeallocPGamepadEventChannelParent(dom::PGamepadEventChannelParent *aActor)
+{
+#ifdef MOZ_GAMEPAD
+ MOZ_ASSERT(aActor);
+ RefPtr<dom::GamepadEventChannelParent> parent =
+ dont_AddRef(static_cast<dom::GamepadEventChannelParent*>(aActor));
+#endif
+ return true;
+}
+
+dom::PGamepadTestChannelParent*
+BackgroundParentImpl::AllocPGamepadTestChannelParent()
+{
+#ifdef MOZ_GAMEPAD
+ RefPtr<dom::GamepadTestChannelParent> parent =
+ new dom::GamepadTestChannelParent();
+
+ return parent.forget().take();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+BackgroundParentImpl::DeallocPGamepadTestChannelParent(dom::PGamepadTestChannelParent *aActor)
+{
+#ifdef MOZ_GAMEPAD
+ MOZ_ASSERT(aActor);
+ RefPtr<dom::GamepadTestChannelParent> parent =
+ dont_AddRef(static_cast<dom::GamepadTestChannelParent*>(aActor));
+#endif
+ return true;
+}
+
+} // namespace ipc
+} // namespace mozilla
+
+void
+TestParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mozilla::ipc::AssertIsInMainProcess();
+ AssertIsOnBackgroundThread();
+}
diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h
new file mode 100644
index 000000000..8d0ac06a6
--- /dev/null
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_backgroundparentimpl_h__
+#define mozilla_ipc_backgroundparentimpl_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+
+namespace mozilla {
+
+namespace layout {
+class VsyncParent;
+} // namespace layout
+
+namespace ipc {
+
+// Instances of this class should never be created directly. This class is meant
+// to be inherited in BackgroundImpl.
+class BackgroundParentImpl : public PBackgroundParent
+{
+protected:
+ BackgroundParentImpl();
+ virtual ~BackgroundParentImpl();
+
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual PBackgroundTestParent*
+ AllocPBackgroundTestParent(const nsCString& aTestArg) override;
+
+ virtual bool
+ RecvPBackgroundTestConstructor(PBackgroundTestParent* aActor,
+ const nsCString& aTestArg) override;
+
+ virtual bool
+ DeallocPBackgroundTestParent(PBackgroundTestParent* aActor) override;
+
+ virtual PBackgroundIDBFactoryParent*
+ AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo)
+ override;
+
+ virtual bool
+ RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor,
+ const LoggingInfo& aLoggingInfo)
+ override;
+
+ virtual bool
+ DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor)
+ override;
+
+ virtual PBackgroundIndexedDBUtilsParent*
+ AllocPBackgroundIndexedDBUtilsParent() override;
+
+ virtual bool
+ DeallocPBackgroundIndexedDBUtilsParent(
+ PBackgroundIndexedDBUtilsParent* aActor)
+ override;
+
+ virtual bool
+ RecvFlushPendingFileDeletions() override;
+
+ virtual PBlobParent*
+ AllocPBlobParent(const BlobConstructorParams& aParams) override;
+
+ virtual bool
+ DeallocPBlobParent(PBlobParent* aActor) override;
+
+ virtual bool
+ RecvPBlobConstructor(PBlobParent* aActor,
+ const BlobConstructorParams& params) override;
+
+ virtual PFileDescriptorSetParent*
+ AllocPFileDescriptorSetParent(const FileDescriptor& aFileDescriptor)
+ override;
+
+ virtual bool
+ DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
+ override;
+
+ virtual PVsyncParent*
+ AllocPVsyncParent() override;
+
+ virtual bool
+ DeallocPVsyncParent(PVsyncParent* aActor) override;
+
+ virtual PBroadcastChannelParent*
+ AllocPBroadcastChannelParent(const PrincipalInfo& aPrincipalInfo,
+ const nsCString& aOrigin,
+ const nsString& aChannel) override;
+
+ virtual bool
+ RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
+ const PrincipalInfo& aPrincipalInfo,
+ const nsCString& origin,
+ const nsString& channel) override;
+
+ virtual bool
+ DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
+
+ virtual PSendStreamParent*
+ AllocPSendStreamParent() override;
+
+ virtual bool
+ DeallocPSendStreamParent(PSendStreamParent* aActor) override;
+
+ virtual PServiceWorkerManagerParent*
+ AllocPServiceWorkerManagerParent() override;
+
+ virtual bool
+ DeallocPServiceWorkerManagerParent(PServiceWorkerManagerParent* aActor) override;
+
+ virtual PCamerasParent*
+ AllocPCamerasParent() override;
+
+ virtual bool
+ DeallocPCamerasParent(PCamerasParent* aActor) override;
+
+ virtual bool
+ RecvShutdownServiceWorkerRegistrar() override;
+
+ virtual dom::cache::PCacheStorageParent*
+ AllocPCacheStorageParent(const dom::cache::Namespace& aNamespace,
+ const PrincipalInfo& aPrincipalInfo) override;
+
+ virtual bool
+ DeallocPCacheStorageParent(dom::cache::PCacheStorageParent* aActor) override;
+
+ virtual dom::cache::PCacheParent* AllocPCacheParent() override;
+
+ virtual bool
+ DeallocPCacheParent(dom::cache::PCacheParent* aActor) override;
+
+ virtual dom::cache::PCacheStreamControlParent*
+ AllocPCacheStreamControlParent() override;
+
+ virtual bool
+ DeallocPCacheStreamControlParent(dom::cache::PCacheStreamControlParent* aActor)
+ override;
+
+ virtual PUDPSocketParent*
+ AllocPUDPSocketParent(const OptionalPrincipalInfo& pInfo,
+ const nsCString& aFilter) override;
+ virtual bool
+ RecvPUDPSocketConstructor(PUDPSocketParent*,
+ const OptionalPrincipalInfo& aPrincipalInfo,
+ const nsCString& aFilter) override;
+ virtual bool
+ DeallocPUDPSocketParent(PUDPSocketParent*) override;
+
+ virtual PMessagePortParent*
+ AllocPMessagePortParent(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID) override;
+
+ virtual bool
+ RecvPMessagePortConstructor(PMessagePortParent* aActor,
+ const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID) override;
+
+ virtual bool
+ DeallocPMessagePortParent(PMessagePortParent* aActor) override;
+
+ virtual bool
+ RecvMessagePortForceClose(const nsID& aUUID,
+ const nsID& aDestinationUUID,
+ const uint32_t& aSequenceID) override;
+
+ virtual PAsmJSCacheEntryParent*
+ AllocPAsmJSCacheEntryParent(const dom::asmjscache::OpenMode& aOpenMode,
+ const dom::asmjscache::WriteParams& aWriteParams,
+ const PrincipalInfo& aPrincipalInfo) override;
+
+ virtual bool
+ DeallocPAsmJSCacheEntryParent(PAsmJSCacheEntryParent* aActor) override;
+
+ virtual PQuotaParent*
+ AllocPQuotaParent() override;
+
+ virtual bool
+ DeallocPQuotaParent(PQuotaParent* aActor) override;
+
+ virtual PFileSystemRequestParent*
+ AllocPFileSystemRequestParent(const FileSystemParams&) override;
+
+ virtual bool
+ RecvPFileSystemRequestConstructor(PFileSystemRequestParent* actor,
+ const FileSystemParams& params) override;
+
+ virtual bool
+ DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
+
+ // Gamepad API Background IPC
+ virtual PGamepadEventChannelParent*
+ AllocPGamepadEventChannelParent() override;
+
+ virtual bool
+ DeallocPGamepadEventChannelParent(PGamepadEventChannelParent *aActor) override;
+
+ virtual PGamepadTestChannelParent*
+ AllocPGamepadTestChannelParent() override;
+
+ virtual bool
+ DeallocPGamepadTestChannelParent(PGamepadTestChannelParent* aActor) override;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundparentimpl_h__
diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp
new file mode 100644
index 000000000..b335f5c23
--- /dev/null
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "BackgroundUtils.h"
+
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "mozilla/net/NeckoChannelParams.h"
+#include "nsPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+#include "mozilla/LoadInfo.h"
+#include "nsNullPrincipal.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace net {
+class OptionalLoadInfoArgs;
+}
+
+using mozilla::BasePrincipal;
+using namespace mozilla::net;
+
+namespace ipc {
+
+already_AddRefed<nsIPrincipal>
+PrincipalInfoToPrincipal(const PrincipalInfo& aPrincipalInfo,
+ nsresult* aOptionalResult)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::T__None);
+
+ nsresult stackResult;
+ nsresult& rv = aOptionalResult ? *aOptionalResult : stackResult;
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal;
+
+ switch (aPrincipalInfo.type()) {
+ case PrincipalInfo::TSystemPrincipalInfo: {
+ rv = secMan->GetSystemPrincipal(getter_AddRefs(principal));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ return principal.forget();
+ }
+
+ case PrincipalInfo::TNullPrincipalInfo: {
+ const NullPrincipalInfo& info =
+ aPrincipalInfo.get_NullPrincipalInfo();
+ principal = nsNullPrincipal::Create(info.attrs());
+
+ return principal.forget();
+ }
+
+ case PrincipalInfo::TContentPrincipalInfo: {
+ const ContentPrincipalInfo& info =
+ aPrincipalInfo.get_ContentPrincipalInfo();
+
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), info.spec());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ PrincipalOriginAttributes attrs;
+ if (info.attrs().mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+ attrs = info.attrs();
+ }
+ principal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+ rv = principal ? NS_OK : NS_ERROR_FAILURE;
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ // When the principal is serialized, the origin is extract from it. This
+ // can fail, and in case, here we will havea Tvoid_t. If we have a string,
+ // it must match with what the_new_principal.getOrigin returns.
+ if (info.originNoSuffix().type() == ContentPrincipalInfoOriginNoSuffix::TnsCString) {
+ nsAutoCString originNoSuffix;
+ rv = principal->GetOriginNoSuffix(originNoSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv)) ||
+ !info.originNoSuffix().get_nsCString().Equals(originNoSuffix)) {
+ MOZ_CRASH("If the origin was in the contentPrincipalInfo, it must be available when deserialized");
+ }
+ }
+
+ return principal.forget();
+ }
+
+ case PrincipalInfo::TExpandedPrincipalInfo: {
+ const ExpandedPrincipalInfo& info = aPrincipalInfo.get_ExpandedPrincipalInfo();
+
+ nsTArray<nsCOMPtr<nsIPrincipal>> whitelist;
+ nsCOMPtr<nsIPrincipal> wlPrincipal;
+
+ for (uint32_t i = 0; i < info.whitelist().Length(); i++) {
+ wlPrincipal = PrincipalInfoToPrincipal(info.whitelist()[i], &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+ // append that principal to the whitelist
+ whitelist.AppendElement(wlPrincipal);
+ }
+
+ RefPtr<nsExpandedPrincipal> expandedPrincipal = new nsExpandedPrincipal(whitelist, info.attrs());
+ if (!expandedPrincipal) {
+ NS_WARNING("could not instantiate expanded principal");
+ return nullptr;
+ }
+
+ principal = expandedPrincipal;
+ return principal.forget();
+ }
+
+ default:
+ MOZ_CRASH("Unknown PrincipalInfo type!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+nsresult
+PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
+ PrincipalInfo* aPrincipalInfo)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+ MOZ_ASSERT(aPrincipalInfo);
+
+ if (aPrincipal->GetIsNullPrincipal()) {
+ *aPrincipalInfo = NullPrincipalInfo(BasePrincipal::Cast(aPrincipal)->OriginAttributesRef());
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool isSystemPrincipal;
+ rv = secMan->IsSystemPrincipal(aPrincipal, &isSystemPrincipal);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (isSystemPrincipal) {
+ *aPrincipalInfo = SystemPrincipalInfo();
+ return NS_OK;
+ }
+
+ // might be an expanded principal
+ nsCOMPtr<nsIExpandedPrincipal> expanded =
+ do_QueryInterface(aPrincipal);
+
+ if (expanded) {
+ nsTArray<PrincipalInfo> whitelistInfo;
+ PrincipalInfo info;
+
+ nsTArray< nsCOMPtr<nsIPrincipal> >* whitelist;
+ MOZ_ALWAYS_SUCCEEDS(expanded->GetWhiteList(&whitelist));
+
+ for (uint32_t i = 0; i < whitelist->Length(); i++) {
+ rv = PrincipalToPrincipalInfo((*whitelist)[i], &info);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // append that spec to the whitelist
+ whitelistInfo.AppendElement(info);
+ }
+
+ *aPrincipalInfo =
+ ExpandedPrincipalInfo(BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(),
+ Move(whitelistInfo));
+ return NS_OK;
+ }
+
+ // must be a content principal
+
+ nsCOMPtr<nsIURI> uri;
+ rv = aPrincipal->GetURI(getter_AddRefs(uri));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!uri)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCString spec;
+ rv = uri->GetSpec(spec);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ ContentPrincipalInfoOriginNoSuffix infoOriginNoSuffix;
+
+ nsCString originNoSuffix;
+ rv = aPrincipal->GetOriginNoSuffix(originNoSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ infoOriginNoSuffix = void_t();
+ } else {
+ infoOriginNoSuffix = originNoSuffix;
+ }
+
+ *aPrincipalInfo = ContentPrincipalInfo(BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(),
+ infoOriginNoSuffix, spec);
+ return NS_OK;
+}
+
+nsresult
+LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
+ OptionalLoadInfoArgs* aOptionalLoadInfoArgs)
+{
+ if (!aLoadInfo) {
+ // if there is no loadInfo, then there is nothing to serialize
+ *aOptionalLoadInfoArgs = void_t();
+ return NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+ OptionalPrincipalInfo loadingPrincipalInfo = mozilla::void_t();
+ if (aLoadInfo->LoadingPrincipal()) {
+ PrincipalInfo loadingPrincipalInfoTemp;
+ rv = PrincipalToPrincipalInfo(aLoadInfo->LoadingPrincipal(),
+ &loadingPrincipalInfoTemp);
+ NS_ENSURE_SUCCESS(rv, rv);
+ loadingPrincipalInfo = loadingPrincipalInfoTemp;
+ }
+
+ PrincipalInfo triggeringPrincipalInfo;
+ rv = PrincipalToPrincipalInfo(aLoadInfo->TriggeringPrincipal(),
+ &triggeringPrincipalInfo);
+
+ OptionalPrincipalInfo principalToInheritInfo = mozilla::void_t();
+ if (aLoadInfo->PrincipalToInherit()) {
+ PrincipalInfo principalToInheritInfoTemp;
+ rv = PrincipalToPrincipalInfo(aLoadInfo->PrincipalToInherit(),
+ &principalToInheritInfoTemp);
+ NS_ENSURE_SUCCESS(rv, rv);
+ principalToInheritInfo = principalToInheritInfoTemp;
+ }
+
+ nsTArray<PrincipalInfo> redirectChainIncludingInternalRedirects;
+ for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChainIncludingInternalRedirects()) {
+ rv = PrincipalToPrincipalInfo(principal, redirectChainIncludingInternalRedirects.AppendElement());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsTArray<PrincipalInfo> redirectChain;
+ for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChain()) {
+ rv = PrincipalToPrincipalInfo(principal, redirectChain.AppendElement());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aOptionalLoadInfoArgs =
+ LoadInfoArgs(
+ loadingPrincipalInfo,
+ triggeringPrincipalInfo,
+ principalToInheritInfo,
+ aLoadInfo->GetSecurityFlags(),
+ aLoadInfo->InternalContentPolicyType(),
+ static_cast<uint32_t>(aLoadInfo->GetTainting()),
+ aLoadInfo->GetUpgradeInsecureRequests(),
+ aLoadInfo->GetVerifySignedContent(),
+ aLoadInfo->GetEnforceSRI(),
+ aLoadInfo->GetForceInheritPrincipalDropped(),
+ aLoadInfo->GetInnerWindowID(),
+ aLoadInfo->GetOuterWindowID(),
+ aLoadInfo->GetParentOuterWindowID(),
+ aLoadInfo->GetFrameOuterWindowID(),
+ aLoadInfo->GetEnforceSecurity(),
+ aLoadInfo->GetInitialSecurityCheckDone(),
+ aLoadInfo->GetIsInThirdPartyContext(),
+ aLoadInfo->GetOriginAttributes(),
+ redirectChainIncludingInternalRedirects,
+ redirectChain,
+ aLoadInfo->CorsUnsafeHeaders(),
+ aLoadInfo->GetForcePreflight(),
+ aLoadInfo->GetIsPreflight(),
+ aLoadInfo->GetForceHSTSPriming(),
+ aLoadInfo->GetMixedContentWouldBlock());
+
+ return NS_OK;
+}
+
+nsresult
+LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
+ nsILoadInfo** outLoadInfo)
+{
+ if (aOptionalLoadInfoArgs.type() == OptionalLoadInfoArgs::Tvoid_t) {
+ *outLoadInfo = nullptr;
+ return NS_OK;
+ }
+
+ const LoadInfoArgs& loadInfoArgs =
+ aOptionalLoadInfoArgs.get_LoadInfoArgs();
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIPrincipal> loadingPrincipal;
+ if (loadInfoArgs.requestingPrincipalInfo().type() != OptionalPrincipalInfo::Tvoid_t) {
+ loadingPrincipal = PrincipalInfoToPrincipal(loadInfoArgs.requestingPrincipalInfo(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal =
+ PrincipalInfoToPrincipal(loadInfoArgs.triggeringPrincipalInfo(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> principalToInherit;
+ if (loadInfoArgs.principalToInheritInfo().type() != OptionalPrincipalInfo::Tvoid_t) {
+ principalToInherit = PrincipalInfoToPrincipal(loadInfoArgs.principalToInheritInfo(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsTArray<nsCOMPtr<nsIPrincipal>> redirectChainIncludingInternalRedirects;
+ for (const PrincipalInfo& principalInfo : loadInfoArgs.redirectChainIncludingInternalRedirects()) {
+ nsCOMPtr<nsIPrincipal> redirectedPrincipal =
+ PrincipalInfoToPrincipal(principalInfo, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ redirectChainIncludingInternalRedirects.AppendElement(redirectedPrincipal.forget());
+ }
+
+ nsTArray<nsCOMPtr<nsIPrincipal>> redirectChain;
+ for (const PrincipalInfo& principalInfo : loadInfoArgs.redirectChain()) {
+ nsCOMPtr<nsIPrincipal> redirectedPrincipal =
+ PrincipalInfoToPrincipal(principalInfo, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ redirectChain.AppendElement(redirectedPrincipal.forget());
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo =
+ new mozilla::LoadInfo(loadingPrincipal,
+ triggeringPrincipal,
+ principalToInherit,
+ loadInfoArgs.securityFlags(),
+ loadInfoArgs.contentPolicyType(),
+ static_cast<LoadTainting>(loadInfoArgs.tainting()),
+ loadInfoArgs.upgradeInsecureRequests(),
+ loadInfoArgs.verifySignedContent(),
+ loadInfoArgs.enforceSRI(),
+ loadInfoArgs.forceInheritPrincipalDropped(),
+ loadInfoArgs.innerWindowID(),
+ loadInfoArgs.outerWindowID(),
+ loadInfoArgs.parentOuterWindowID(),
+ loadInfoArgs.frameOuterWindowID(),
+ loadInfoArgs.enforceSecurity(),
+ loadInfoArgs.initialSecurityCheckDone(),
+ loadInfoArgs.isInThirdPartyContext(),
+ loadInfoArgs.originAttributes(),
+ redirectChainIncludingInternalRedirects,
+ redirectChain,
+ loadInfoArgs.corsUnsafeHeaders(),
+ loadInfoArgs.forcePreflight(),
+ loadInfoArgs.isPreflight(),
+ loadInfoArgs.forceHSTSPriming(),
+ loadInfoArgs.mixedContentWouldBlock()
+ );
+
+ loadInfo.forget(outLoadInfo);
+ return NS_OK;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/BackgroundUtils.h b/ipc/glue/BackgroundUtils.h
new file mode 100644
index 000000000..7ae0f5226
--- /dev/null
+++ b/ipc/glue/BackgroundUtils.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_backgroundutils_h__
+#define mozilla_ipc_backgroundutils_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BasePrincipal.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+
+class nsILoadInfo;
+class nsIPrincipal;
+
+namespace IPC {
+
+namespace detail {
+template<class ParamType>
+struct OriginAttributesParamTraits
+{
+ typedef ParamType paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ nsAutoCString suffix;
+ aParam.CreateSuffix(suffix);
+ WriteParam(aMsg, suffix);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ nsAutoCString suffix;
+ return ReadParam(aMsg, aIter, &suffix) &&
+ aResult->PopulateFromSuffix(suffix);
+ }
+};
+} // namespace detail
+
+template<>
+struct ParamTraits<mozilla::PrincipalOriginAttributes>
+ : public detail::OriginAttributesParamTraits<mozilla::PrincipalOriginAttributes> {};
+
+template<>
+struct ParamTraits<mozilla::DocShellOriginAttributes>
+ : public detail::OriginAttributesParamTraits<mozilla::DocShellOriginAttributes> {};
+
+template<>
+struct ParamTraits<mozilla::NeckoOriginAttributes>
+ : public detail::OriginAttributesParamTraits<mozilla::NeckoOriginAttributes> {};
+
+template<>
+struct ParamTraits<mozilla::GenericOriginAttributes>
+ : public detail::OriginAttributesParamTraits<mozilla::GenericOriginAttributes> {};
+
+} // namespace IPC
+
+namespace mozilla {
+namespace net {
+class OptionalLoadInfoArgs;
+} // namespace net
+
+using namespace mozilla::net;
+
+namespace ipc {
+
+class PrincipalInfo;
+
+/**
+ * Convert a PrincipalInfo to an nsIPrincipal.
+ *
+ * MUST be called on the main thread only.
+ */
+already_AddRefed<nsIPrincipal>
+PrincipalInfoToPrincipal(const PrincipalInfo& aPrincipalInfo,
+ nsresult* aOptionalResult = nullptr);
+
+/**
+ * Convert an nsIPrincipal to a PrincipalInfo.
+ *
+ * MUST be called on the main thread only.
+ */
+nsresult
+PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
+ PrincipalInfo* aPrincipalInfo);
+
+/**
+ * Convert a LoadInfo to LoadInfoArgs struct.
+ */
+nsresult
+LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
+ OptionalLoadInfoArgs* outOptionalLoadInfoArgs);
+
+/**
+ * Convert LoadInfoArgs to a LoadInfo.
+ */
+nsresult
+LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
+ nsILoadInfo** outLoadInfo);
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_backgroundutils_h__
diff --git a/ipc/glue/BrowserProcessSubThread.cpp b/ipc/glue/BrowserProcessSubThread.cpp
new file mode 100644
index 000000000..7618dc934
--- /dev/null
+++ b/ipc/glue/BrowserProcessSubThread.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 "mozilla/ipc/BrowserProcessSubThread.h"
+
+#if defined(OS_WIN)
+#include <objbase.h>
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+//
+// BrowserProcessSubThread
+//
+
+// Friendly names for the well-known threads.
+static const char* kBrowserThreadNames[BrowserProcessSubThread::ID_COUNT] = {
+ "Gecko_IOThread", // IO
+// "Chrome_FileThread", // FILE
+// "Chrome_DBThread", // DB
+// "Chrome_HistoryThread", // HISTORY
+#if defined(OS_LINUX)
+ "Gecko_Background_X11Thread", // BACKGROUND_X11
+#endif
+};
+
+/* static */ StaticMutex BrowserProcessSubThread::sLock;
+BrowserProcessSubThread* BrowserProcessSubThread::sBrowserThreads[ID_COUNT] = {
+ nullptr, // IO
+// nullptr, // FILE
+// nullptr, // DB
+// nullptr, // HISTORY
+#if defined(OS_LINUX)
+ nullptr, // BACKGROUND_X11
+#endif
+};
+
+BrowserProcessSubThread::BrowserProcessSubThread(ID aId) :
+ base::Thread(kBrowserThreadNames[aId]),
+ mIdentifier(aId)
+{
+ StaticMutexAutoLock lock(sLock);
+ DCHECK(aId >= 0 && aId < ID_COUNT);
+ DCHECK(sBrowserThreads[aId] == nullptr);
+ sBrowserThreads[aId] = this;
+}
+
+BrowserProcessSubThread::~BrowserProcessSubThread()
+{
+ Stop();
+ {
+ StaticMutexAutoLock lock(sLock);
+ sBrowserThreads[mIdentifier] = nullptr;
+ }
+
+}
+
+void
+BrowserProcessSubThread::Init()
+{
+#if defined(OS_WIN)
+ // Initializes the COM library on the current thread.
+ CoInitialize(nullptr);
+#endif
+}
+
+void
+BrowserProcessSubThread::CleanUp()
+{
+#if defined(OS_WIN)
+ // Closes the COM library on the current thread. CoInitialize must
+ // be balanced by a corresponding call to CoUninitialize.
+ CoUninitialize();
+#endif
+}
+
+// static
+MessageLoop*
+BrowserProcessSubThread::GetMessageLoop(ID aId)
+{
+ StaticMutexAutoLock lock(sLock);
+ DCHECK(aId >= 0 && aId < ID_COUNT);
+
+ if (sBrowserThreads[aId])
+ return sBrowserThreads[aId]->message_loop();
+
+ return nullptr;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/BrowserProcessSubThread.h b/ipc/glue/BrowserProcessSubThread.h
new file mode 100644
index 000000000..e1b4aef46
--- /dev/null
+++ b/ipc/glue/BrowserProcessSubThread.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_BrowserProcessSubThread_h
+#define mozilla_ipc_BrowserProcessSubThread_h
+
+#include "base/thread.h"
+#include "mozilla/StaticMutex.h"
+
+#include "nsDebug.h"
+
+namespace mozilla {
+namespace ipc {
+
+// Copied from browser_process_impl.cc, modified slightly.
+class BrowserProcessSubThread : public base::Thread
+{
+public:
+ // An enumeration of the well-known threads.
+ enum ID {
+ IO,
+ //FILE,
+ //DB,
+ //HISTORY,
+#if defined(OS_LINUX)
+ // This thread has a second connection to the X server and is used
+ // to process UI requests when routing the request to the UI
+ // thread would risk deadlock.
+ BACKGROUND_X11,
+#endif
+
+ // This identifier does not represent a thread. Instead it counts
+ // the number of well-known threads. Insert new well-known
+ // threads before this identifier.
+ ID_COUNT
+ };
+
+ explicit BrowserProcessSubThread(ID aId);
+ ~BrowserProcessSubThread();
+
+ static MessageLoop* GetMessageLoop(ID identifier);
+
+protected:
+ virtual void Init();
+ virtual void CleanUp();
+
+private:
+ // The identifier of this thread. Only one thread can exist with a given
+ // identifier at a given time.
+ ID mIdentifier;
+
+ // This lock protects |browser_threads_|. Do not read or modify that array
+ // without holding this lock. Do not block while holding this lock.
+
+ static StaticMutex sLock;
+
+ // An array of the ChromeThread objects. This array is protected by |lock_|.
+ // The threads are not owned by this array. Typically, the threads are owned
+ // on the UI thread by the g_browser_process object. ChromeThreads remove
+ // themselves from this array upon destruction.
+ static BrowserProcessSubThread* sBrowserThreads[ID_COUNT];
+};
+
+inline void AssertIOThread()
+{
+ NS_ASSERTION(MessageLoop::TYPE_IO == MessageLoop::current()->type(),
+ "should be on the IO thread!");
+}
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_BrowserProcessSubThread_h
diff --git a/ipc/glue/CrashReporterClient.cpp b/ipc/glue/CrashReporterClient.cpp
new file mode 100644
index 000000000..004ca3b57
--- /dev/null
+++ b/ipc/glue/CrashReporterClient.cpp
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "CrashReporterClient.h"
+#include "CrashReporterMetadataShmem.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace ipc {
+
+StaticMutex CrashReporterClient::sLock;
+StaticRefPtr<CrashReporterClient> CrashReporterClient::sClientSingleton;
+
+CrashReporterClient::CrashReporterClient(const Shmem& aShmem)
+ : mMetadata(new CrashReporterMetadataShmem(aShmem))
+{
+ MOZ_COUNT_CTOR(CrashReporterClient);
+}
+
+CrashReporterClient::~CrashReporterClient()
+{
+ MOZ_COUNT_DTOR(CrashReporterClient);
+}
+
+void
+CrashReporterClient::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData)
+{
+ StaticMutexAutoLock lock(sLock);
+ mMetadata->AnnotateCrashReport(aKey, aData);
+}
+
+void
+CrashReporterClient::AppendAppNotes(const nsCString& aData)
+{
+ StaticMutexAutoLock lock(sLock);
+ mMetadata->AppendAppNotes(aData);
+}
+
+/* static */ void
+CrashReporterClient::InitSingletonWithShmem(const Shmem& aShmem)
+{
+ StaticMutexAutoLock lock(sLock);
+
+ MOZ_ASSERT(!sClientSingleton);
+ sClientSingleton = new CrashReporterClient(aShmem);
+}
+
+/* static */ void
+CrashReporterClient::DestroySingleton()
+{
+ StaticMutexAutoLock lock(sLock);
+ sClientSingleton = nullptr;
+}
+
+/* static */ RefPtr<CrashReporterClient>
+CrashReporterClient::GetSingleton()
+{
+ StaticMutexAutoLock lock(sLock);
+ return sClientSingleton;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/CrashReporterClient.h b/ipc/glue/CrashReporterClient.h
new file mode 100644
index 000000000..512533da8
--- /dev/null
+++ b/ipc/glue/CrashReporterClient.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_CrashReporterClient_h
+#define mozilla_ipc_CrashReporterClient_h
+
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+namespace ipc {
+
+class CrashReporterMetadataShmem;
+
+class CrashReporterClient
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CrashReporterClient);
+
+ // |aTopLevelProtocol| must be a top-level protocol instance, as sub-actors
+ // do not have AllocUnsafeShmem. It must also have a child-to-parent message:
+ //
+ // async SetCrashReporterClient(Shmem shmem);
+ //
+ // The parent-side receive function of this message should save the shmem
+ // somewhere, and when the top-level actor's ActorDestroy runs (or when the
+ // crash reporter needs metadata), the shmem should be parsed.
+ template <typename T>
+ static bool InitSingleton(T* aToplevelProtocol) {
+ // 16KB should be enough for most metadata - see bug 1278717 comment #11.
+ static const size_t kShmemSize = 16 * 1024;
+
+ Shmem shmem;
+ bool rv = aToplevelProtocol->AllocUnsafeShmem(
+ kShmemSize,
+ SharedMemory::TYPE_BASIC,
+ &shmem);
+ if (!rv) {
+ return false;
+ }
+
+ InitSingletonWithShmem(shmem);
+ Unused << aToplevelProtocol->SendInitCrashReporter(shmem);
+ return true;
+ }
+
+ static void DestroySingleton();
+ static RefPtr<CrashReporterClient> GetSingleton();
+
+ void AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
+ void AppendAppNotes(const nsCString& aData);
+
+private:
+ explicit CrashReporterClient(const Shmem& aShmem);
+ ~CrashReporterClient();
+
+ static void InitSingletonWithShmem(const Shmem& aShmem);
+
+private:
+ static StaticMutex sLock;
+ static StaticRefPtr<CrashReporterClient> sClientSingleton;
+
+private:
+ UniquePtr<CrashReporterMetadataShmem> mMetadata;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_CrashReporterClient_h
+
diff --git a/ipc/glue/CrashReporterHost.cpp b/ipc/glue/CrashReporterHost.cpp
new file mode 100644
index 000000000..76052ae66
--- /dev/null
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "CrashReporterHost.h"
+#include "CrashReporterMetadataShmem.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/Telemetry.h"
+#ifdef MOZ_CRASHREPORTER
+# include "nsICrashService.h"
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+CrashReporterHost::CrashReporterHost(GeckoProcessType aProcessType,
+ const Shmem& aShmem)
+ : mProcessType(aProcessType),
+ mShmem(aShmem),
+ mStartTime(::time(nullptr))
+{
+}
+
+#ifdef MOZ_CRASHREPORTER
+void
+CrashReporterHost::GenerateCrashReport(RefPtr<nsIFile> aCrashDump)
+{
+ nsString dumpID;
+ if (!CrashReporter::GetIDFromMinidump(aCrashDump, dumpID)) {
+ return;
+ }
+
+ CrashReporter::AnnotationTable notes;
+
+ 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;
+ case GeckoProcessType_GPU:
+ type = NS_LITERAL_CSTRING("gpu");
+ break;
+ default:
+ NS_ERROR("unknown process type");
+ break;
+ }
+ notes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
+
+ char startTime[32];
+ SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime));
+ notes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
+
+ CrashReporterMetadataShmem::ReadAppNotes(mShmem, &notes);
+
+ CrashReporter::AppendExtraData(dumpID, notes);
+ NotifyCrashService(mProcessType, dumpID, &notes);
+}
+
+/* static */ void
+CrashReporterHost::NotifyCrashService(GeckoProcessType aProcessType,
+ const nsString& aChildDumpID,
+ const AnnotationTable* aNotes)
+{
+ if (!NS_IsMainThread()) {
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction([=] () -> void {
+ CrashReporterHost::NotifyCrashService(aProcessType, aChildDumpID, aNotes);
+ });
+ RefPtr<nsIThread> mainThread = do_GetMainThread();
+ SyncRunnable::DispatchToThread(mainThread, runnable);
+ return;
+ }
+
+ MOZ_ASSERT(!aChildDumpID.IsEmpty());
+
+ nsCOMPtr<nsICrashService> crashService =
+ do_GetService("@mozilla.org/crashservice;1");
+ if (!crashService) {
+ return;
+ }
+
+ int32_t processType;
+ int32_t crashType = nsICrashService::CRASH_TYPE_CRASH;
+
+ nsCString telemetryKey;
+
+ switch (aProcessType) {
+ case GeckoProcessType_Content:
+ processType = nsICrashService::PROCESS_TYPE_CONTENT;
+ telemetryKey.AssignLiteral("content");
+ break;
+ case GeckoProcessType_Plugin: {
+ processType = nsICrashService::PROCESS_TYPE_PLUGIN;
+ telemetryKey.AssignLiteral("plugin");
+ nsAutoCString val;
+ if (aNotes->Get(NS_LITERAL_CSTRING("PluginHang"), &val) &&
+ val.Equals(NS_LITERAL_CSTRING("1"))) {
+ crashType = nsICrashService::CRASH_TYPE_HANG;
+ telemetryKey.AssignLiteral("pluginhang");
+ }
+ break;
+ }
+ case GeckoProcessType_GMPlugin:
+ processType = nsICrashService::PROCESS_TYPE_GMPLUGIN;
+ telemetryKey.AssignLiteral("gmplugin");
+ break;
+ case GeckoProcessType_GPU:
+ processType = nsICrashService::PROCESS_TYPE_GPU;
+ telemetryKey.AssignLiteral("gpu");
+ break;
+ default:
+ NS_ERROR("unknown process type");
+ return;
+ }
+
+ crashService->AddCrash(processType, crashType, aChildDumpID);
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_CRASHES_WITH_DUMP, telemetryKey, 1);
+}
+#endif
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/CrashReporterHost.h b/ipc/glue/CrashReporterHost.h
new file mode 100644
index 000000000..36c5923c2
--- /dev/null
+++ b/ipc/glue/CrashReporterHost.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_CrashReporterHost_h
+#define mozilla_ipc_CrashReporterHost_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/Shmem.h"
+#include "base/process.h"
+#include "nsExceptionHandler.h"
+
+namespace mozilla {
+namespace ipc {
+
+// This is the newer replacement for CrashReporterParent. It is created in
+// response to a InitCrashReporter message on a top-level actor, and simply
+// holds the metadata shmem alive until the process ends. When the process
+// terminates abnormally, the top-level should call GenerateCrashReport to
+// automatically integrate metadata.
+class CrashReporterHost
+{
+ typedef mozilla::ipc::Shmem Shmem;
+ typedef CrashReporter::AnnotationTable AnnotationTable;
+
+public:
+ CrashReporterHost(GeckoProcessType aProcessType, const Shmem& aShmem);
+
+#ifdef MOZ_CRASHREPORTER
+ void GenerateCrashReport(base::ProcessId aPid) {
+ RefPtr<nsIFile> crashDump;
+ if (!XRE_TakeMinidumpForChild(aPid, getter_AddRefs(crashDump), nullptr)) {
+ return;
+ }
+ GenerateCrashReport(crashDump);
+ }
+
+ // This is a static helper function to notify the crash service that a
+ // crash has occurred. When PCrashReporter is removed, we can make this
+ // a member function. This can be called from any thread, and if not
+ // called from the main thread, will post a synchronous message to the
+ // main thread.
+ static void NotifyCrashService(
+ GeckoProcessType aProcessType,
+ const nsString& aChildDumpID,
+ const AnnotationTable* aNotes);
+#endif
+
+private:
+ void GenerateCrashReport(RefPtr<nsIFile> aCrashDump);
+
+private:
+ GeckoProcessType mProcessType;
+ Shmem mShmem;
+ time_t mStartTime;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_CrashReporterHost_h
diff --git a/ipc/glue/CrashReporterMetadataShmem.cpp b/ipc/glue/CrashReporterMetadataShmem.cpp
new file mode 100644
index 000000000..f579d5bb0
--- /dev/null
+++ b/ipc/glue/CrashReporterMetadataShmem.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "CrashReporterMetadataShmem.h"
+#include "mozilla/Attributes.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace ipc {
+
+enum class EntryType : uint8_t {
+ None,
+ Annotation,
+};
+
+CrashReporterMetadataShmem::CrashReporterMetadataShmem(const Shmem& aShmem)
+ : mShmem(aShmem)
+{
+ MOZ_COUNT_CTOR(CrashReporterMetadataShmem);
+}
+
+CrashReporterMetadataShmem::~CrashReporterMetadataShmem()
+{
+ MOZ_COUNT_DTOR(CrashReporterMetadataShmem);
+}
+
+void
+CrashReporterMetadataShmem::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData)
+{
+ mNotes.Put(aKey, aData);
+ SyncNotesToShmem();
+}
+
+void
+CrashReporterMetadataShmem::AppendAppNotes(const nsCString& aData)
+{
+ mAppNotes.Append(aData);
+ mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
+ SyncNotesToShmem();
+}
+
+class MOZ_STACK_CLASS MetadataShmemWriter
+{
+public:
+ explicit MetadataShmemWriter(const Shmem& aShmem)
+ : mCursor(aShmem.get<uint8_t>()),
+ mEnd(mCursor + aShmem.Size<uint8_t>())
+ {
+ *mCursor = uint8_t(EntryType::None);
+ }
+
+ MOZ_MUST_USE bool WriteAnnotation(const nsCString& aKey, const nsCString& aValue) {
+ // This shouldn't happen because Commit() guarantees mCursor < mEnd. But
+ // we might as well be safe.
+ if (mCursor >= mEnd) {
+ return false;
+ }
+
+ // Save the current position so we can write the entry type if the entire
+ // entry fits.
+ uint8_t* start = mCursor++;
+ if (!Write(aKey) || !Write(aValue)) {
+ return false;
+ }
+ return Commit(start, EntryType::Annotation);
+ }
+
+private:
+ // On success, append a new terminal byte. On failure, rollback the cursor.
+ MOZ_MUST_USE bool Commit(uint8_t* aStart, EntryType aType) {
+ MOZ_ASSERT(aStart < mEnd);
+ MOZ_ASSERT(EntryType(*aStart) == EntryType::None);
+
+ if (mCursor >= mEnd) {
+ // No room for a terminating byte - rollback.
+ mCursor = aStart;
+ return false;
+ }
+
+ // Commit the entry and write a new terminal byte.
+ *aStart = uint8_t(aType);
+ *mCursor = uint8_t(EntryType::None);
+ return true;
+ }
+
+ MOZ_MUST_USE bool Write(const nsCString& aString) {
+ // 32-bit length is okay since our shmems are very small (16K),
+ // a huge write would fail anyway.
+ return Write(static_cast<uint32_t>(aString.Length())) &&
+ Write(aString.get(), aString.Length());
+ }
+
+ template <typename T>
+ MOZ_MUST_USE bool Write(const T& aT) {
+ return Write(&aT, sizeof(T));
+ }
+
+ MOZ_MUST_USE bool Write(const void* aData, size_t aLength) {
+ if (size_t(mEnd - mCursor) < aLength) {
+ return false;
+ }
+ memcpy(mCursor, aData, aLength);
+ mCursor += aLength;
+ return true;
+ }
+
+ private:
+ // The cursor (beginning at start) always points to a single byte
+ // representing the next EntryType. An EntryType is either None,
+ // indicating there are no more entries, or Annotation, meaning
+ // two strings follow.
+ //
+ // Strings are written as a 32-bit length and byte sequence. After each new
+ // entry, a None entry is always appended, and a subsequent entry will
+ // overwrite this byte.
+ uint8_t* mCursor;
+ uint8_t* mEnd;
+};
+
+void
+CrashReporterMetadataShmem::SyncNotesToShmem()
+{
+ MetadataShmemWriter writer(mShmem);
+
+ for (auto it = mNotes.Iter(); !it.Done(); it.Next()) {
+ nsCString key = nsCString(it.Key());
+ nsCString value = nsCString(it.Data());
+ if (!writer.WriteAnnotation(key, value)) {
+ return;
+ }
+ }
+}
+
+// Helper class to iterate over metadata entries encoded in shmem.
+class MOZ_STACK_CLASS MetadataShmemReader
+{
+public:
+ explicit MetadataShmemReader(const Shmem& aShmem)
+ : mEntryType(EntryType::None)
+ {
+ mCursor = aShmem.get<uint8_t>();
+ mEnd = mCursor + aShmem.Size<uint8_t>();
+
+ // Advance to the first item, if any.
+ Next();
+ }
+
+ bool Done() const {
+ return mCursor >= mEnd || Type() == EntryType::None;
+ }
+ EntryType Type() const {
+ return mEntryType;
+ }
+ void Next() {
+ if (mCursor < mEnd) {
+ mEntryType = EntryType(*mCursor++);
+ } else {
+ mEntryType = EntryType::None;
+ }
+ }
+
+ bool Read(nsCString& aOut) {
+ uint32_t length = 0;
+ if (!Read(&length)) {
+ return false;
+ }
+
+ const uint8_t* src = Read(length);
+ if (!src) {
+ return false;
+ }
+
+ aOut.Assign((const char *)src, length);
+ return true;
+ }
+
+private:
+ template <typename T>
+ bool Read(T* aOut) {
+ return Read(aOut, sizeof(T));
+ }
+ bool Read(void* aOut, size_t aLength) {
+ const uint8_t* src = Read(aLength);
+ if (!src) {
+ return false;
+ }
+ memcpy(aOut, src, aLength);
+ return true;
+ }
+
+ // If buffer has |aLength| bytes, return cursor and then advance it.
+ // Otherwise, return null.
+ const uint8_t* Read(size_t aLength) {
+ if (size_t(mEnd - mCursor) < aLength) {
+ return nullptr;
+ }
+ const uint8_t* result = mCursor;
+ mCursor += aLength;
+ return result;
+ }
+
+private:
+ const uint8_t* mCursor;
+ const uint8_t* mEnd;
+ EntryType mEntryType;
+};
+
+#ifdef MOZ_CRASHREPORTER
+void
+CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes)
+{
+ for (MetadataShmemReader reader(aShmem); !reader.Done(); reader.Next()) {
+ switch (reader.Type()) {
+ case EntryType::Annotation: {
+ nsCString key, value;
+ if (!reader.Read(key) || !reader.Read(value)) {
+ return;
+ }
+
+ aNotes->Put(key, value);
+ break;
+ }
+ default:
+ NS_ASSERTION(false, "Unknown metadata entry type");
+ break;
+ }
+ }
+}
+#endif
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/CrashReporterMetadataShmem.h b/ipc/glue/CrashReporterMetadataShmem.h
new file mode 100644
index 000000000..d2d8670a2
--- /dev/null
+++ b/ipc/glue/CrashReporterMetadataShmem.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_CrashReporterMetadataShmem_h
+#define mozilla_ipc_CrashReporterMetadataShmem_h
+
+#include <stdint.h>
+#include "mozilla/ipc/Shmem.h"
+#include "nsExceptionHandler.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace ipc {
+
+class CrashReporterMetadataShmem
+{
+ typedef mozilla::ipc::Shmem Shmem;
+ typedef CrashReporter::AnnotationTable AnnotationTable;
+
+public:
+ explicit CrashReporterMetadataShmem(const Shmem& aShmem);
+ ~CrashReporterMetadataShmem();
+
+ // Metadata writers. These must only be called in child processes.
+ void AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
+ void AppendAppNotes(const nsCString& aData);
+
+#ifdef MOZ_CRASHREPORTER
+ static void ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes);
+#endif
+
+private:
+ void SyncNotesToShmem();
+
+private:
+ Shmem mShmem;
+
+ AnnotationTable mNotes;
+ nsCString mAppNotes;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_CrashReporterMetadataShmem_h
diff --git a/ipc/glue/CrossProcessMutex.h b/ipc/glue/CrossProcessMutex.h
new file mode 100644
index 000000000..b7379a9bb
--- /dev/null
+++ b/ipc/glue/CrossProcessMutex.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_CrossProcessMutex_h
+#define mozilla_CrossProcessMutex_h
+
+#include "base/process.h"
+#include "mozilla/Mutex.h"
+
+#if !defined(OS_WIN) && !defined(OS_NETBSD) && !defined(OS_OPENBSD)
+#include <pthread.h>
+#include "SharedMemoryBasic.h"
+#include "mozilla/Atomics.h"
+#endif
+
+namespace IPC {
+template<typename T>
+struct ParamTraits;
+} // namespace IPC
+
+//
+// Provides:
+//
+// - CrossProcessMutex, a non-recursive mutex that can be shared across processes
+// - CrossProcessMutexAutoLock, an RAII class for ensuring that Mutexes are
+// properly locked and unlocked
+//
+// Using CrossProcessMutexAutoLock/CrossProcessMutexAutoUnlock is MUCH
+// preferred to making bare calls to CrossProcessMutex.Lock and Unlock.
+//
+namespace mozilla {
+#if defined(OS_WIN)
+typedef HANDLE CrossProcessMutexHandle;
+#elif !defined(OS_NETBSD) && !defined(OS_OPENBSD)
+typedef mozilla::ipc::SharedMemoryBasic::Handle CrossProcessMutexHandle;
+#else
+// Stub for other platforms. We can't use uintptr_t here since different
+// processes could disagree on its size.
+typedef uintptr_t CrossProcessMutexHandle;
+#endif
+
+class CrossProcessMutex
+{
+public:
+ /**
+ * CrossProcessMutex
+ * @param name A name which can reference this lock (currently unused)
+ **/
+ explicit CrossProcessMutex(const char* aName);
+ /**
+ * CrossProcessMutex
+ * @param handle A handle of an existing cross process mutex that can be
+ * opened.
+ */
+ explicit CrossProcessMutex(CrossProcessMutexHandle aHandle);
+
+ /**
+ * ~CrossProcessMutex
+ **/
+ ~CrossProcessMutex();
+
+ /**
+ * Lock
+ * This will lock the mutex. Any other thread in any other process that
+ * has access to this mutex calling lock will block execution until the
+ * initial caller of lock has made a call to Unlock.
+ *
+ * If the owning process is terminated unexpectedly the mutex will be
+ * released.
+ **/
+ void Lock();
+
+ /**
+ * Unlock
+ * This will unlock the mutex. A single thread currently waiting on a lock
+ * call will resume execution and aquire ownership of the lock. No
+ * guarantees are made as to the order in which waiting threads will resume
+ * execution.
+ **/
+ void Unlock();
+
+ /**
+ * ShareToProcess
+ * This function is called to generate a serializable structure that can
+ * be sent to the specified process and opened on the other side.
+ *
+ * @returns A handle that can be shared to another process
+ */
+ CrossProcessMutexHandle ShareToProcess(base::ProcessId aTargetPid);
+
+private:
+ friend struct IPC::ParamTraits<CrossProcessMutex>;
+
+ CrossProcessMutex();
+ CrossProcessMutex(const CrossProcessMutex&);
+ CrossProcessMutex &operator=(const CrossProcessMutex&);
+
+#if defined(OS_WIN)
+ HANDLE mMutex;
+#elif !defined(OS_NETBSD) && !defined(OS_OPENBSD)
+ RefPtr<mozilla::ipc::SharedMemoryBasic> mSharedBuffer;
+ pthread_mutex_t* mMutex;
+ mozilla::Atomic<int32_t>* mCount;
+#endif
+};
+
+typedef BaseAutoLock<CrossProcessMutex> CrossProcessMutexAutoLock;
+typedef BaseAutoUnlock<CrossProcessMutex> CrossProcessMutexAutoUnlock;
+
+} // namespace mozilla
+
+#endif
diff --git a/ipc/glue/CrossProcessMutex_posix.cpp b/ipc/glue/CrossProcessMutex_posix.cpp
new file mode 100644
index 000000000..79775a045
--- /dev/null
+++ b/ipc/glue/CrossProcessMutex_posix.cpp
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "CrossProcessMutex.h"
+#include "mozilla/Unused.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+
+namespace {
+
+struct MutexData {
+ pthread_mutex_t mMutex;
+ mozilla::Atomic<int32_t> mCount;
+};
+
+} // namespace
+
+namespace mozilla {
+
+static void
+InitMutex(pthread_mutex_t* mMutex)
+{
+ pthread_mutexattr_t mutexAttributes;
+ pthread_mutexattr_init(&mutexAttributes);
+ // Make the mutex reentrant so it behaves the same as a win32 mutex
+ if (pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE)) {
+ MOZ_CRASH();
+ }
+ if (pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED)) {
+ MOZ_CRASH();
+ }
+
+ if (pthread_mutex_init(mMutex, &mutexAttributes)) {
+ MOZ_CRASH();
+ }
+}
+
+CrossProcessMutex::CrossProcessMutex(const char*)
+ : mMutex(nullptr)
+ , mCount(nullptr)
+{
+ mSharedBuffer = new ipc::SharedMemoryBasic;
+ if (!mSharedBuffer->Create(sizeof(MutexData))) {
+ MOZ_CRASH();
+ }
+
+ if (!mSharedBuffer->Map(sizeof(MutexData))) {
+ MOZ_CRASH();
+ }
+
+ MutexData* data = static_cast<MutexData*>(mSharedBuffer->memory());
+
+ if (!data) {
+ MOZ_CRASH();
+ }
+
+ mMutex = &(data->mMutex);
+ mCount = &(data->mCount);
+
+ *mCount = 1;
+ InitMutex(mMutex);
+
+ MOZ_COUNT_CTOR(CrossProcessMutex);
+}
+
+CrossProcessMutex::CrossProcessMutex(CrossProcessMutexHandle aHandle)
+ : mMutex(nullptr)
+ , mCount(nullptr)
+{
+ mSharedBuffer = new ipc::SharedMemoryBasic;
+
+ if (!mSharedBuffer->IsHandleValid(aHandle)) {
+ MOZ_CRASH();
+ }
+
+ if (!mSharedBuffer->SetHandle(aHandle)) {
+ MOZ_CRASH();
+ }
+
+ if (!mSharedBuffer->Map(sizeof(MutexData))) {
+ MOZ_CRASH();
+ }
+
+ MutexData* data = static_cast<MutexData*>(mSharedBuffer->memory());
+
+ if (!data) {
+ MOZ_CRASH();
+ }
+
+ mMutex = &(data->mMutex);
+ mCount = &(data->mCount);
+ int32_t count = (*mCount)++;
+
+ if (count == 0) {
+ // The other side has already let go of their CrossProcessMutex, so now
+ // mMutex is garbage. We need to re-initialize it.
+ InitMutex(mMutex);
+ }
+
+ MOZ_COUNT_CTOR(CrossProcessMutex);
+}
+
+CrossProcessMutex::~CrossProcessMutex()
+{
+ int32_t count = --(*mCount);
+
+ if (count == 0) {
+ // Nothing can be done if the destroy fails so ignore return code.
+ Unused << pthread_mutex_destroy(mMutex);
+ }
+
+ MOZ_COUNT_DTOR(CrossProcessMutex);
+}
+
+void
+CrossProcessMutex::Lock()
+{
+ MOZ_ASSERT(*mCount > 0, "Attempting to lock mutex with zero ref count");
+ pthread_mutex_lock(mMutex);
+}
+
+void
+CrossProcessMutex::Unlock()
+{
+ MOZ_ASSERT(*mCount > 0, "Attempting to unlock mutex with zero ref count");
+ pthread_mutex_unlock(mMutex);
+}
+
+CrossProcessMutexHandle
+CrossProcessMutex::ShareToProcess(base::ProcessId aTargetPid)
+{
+ CrossProcessMutexHandle result = ipc::SharedMemoryBasic::NULLHandle();
+
+ if (mSharedBuffer && !mSharedBuffer->ShareToProcess(aTargetPid, &result)) {
+ MOZ_CRASH();
+ }
+
+ return result;
+}
+
+} // namespace mozilla
diff --git a/ipc/glue/CrossProcessMutex_unimplemented.cpp b/ipc/glue/CrossProcessMutex_unimplemented.cpp
new file mode 100644
index 000000000..6e8beb15b
--- /dev/null
+++ b/ipc/glue/CrossProcessMutex_unimplemented.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CrossProcessMutex.h"
+
+#include "nsDebug.h"
+
+namespace mozilla {
+
+CrossProcessMutex::CrossProcessMutex(const char*)
+{
+ NS_RUNTIMEABORT("Cross-process mutices not allowed on this platform.");
+}
+
+CrossProcessMutex::CrossProcessMutex(CrossProcessMutexHandle)
+{
+ NS_RUNTIMEABORT("Cross-process mutices not allowed on this platform.");
+}
+
+CrossProcessMutex::~CrossProcessMutex()
+{
+ NS_RUNTIMEABORT("Cross-process mutices not allowed on this platform - woah! We should've aborted by now!");
+}
+
+void
+CrossProcessMutex::Lock()
+{
+ NS_RUNTIMEABORT("Cross-process mutices not allowed on this platform - woah! We should've aborted by now!");
+}
+
+void
+CrossProcessMutex::Unlock()
+{
+ NS_RUNTIMEABORT("Cross-process mutices not allowed on this platform - woah! We should've aborted by now!");
+}
+
+CrossProcessMutexHandle
+CrossProcessMutex::ShareToProcess(base::ProcessId aTargetPid)
+{
+ NS_RUNTIMEABORT("Cross-process mutices not allowed on this platform - woah! We should've aborted by now!");
+ return 0;
+}
+
+}
diff --git a/ipc/glue/CrossProcessMutex_windows.cpp b/ipc/glue/CrossProcessMutex_windows.cpp
new file mode 100644
index 000000000..bc818562b
--- /dev/null
+++ b/ipc/glue/CrossProcessMutex_windows.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+
+#include "base/process_util.h"
+#include "CrossProcessMutex.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+#include "ProtocolUtils.h"
+
+using base::GetCurrentProcessHandle;
+using base::ProcessHandle;
+
+namespace mozilla {
+
+CrossProcessMutex::CrossProcessMutex(const char*)
+{
+ // We explicitly share this using DuplicateHandle, we do -not- want this to
+ // be inherited by child processes by default! So no security attributes are
+ // given.
+ mMutex = ::CreateMutexA(nullptr, FALSE, nullptr);
+ if (!mMutex) {
+ NS_RUNTIMEABORT("This shouldn't happen - failed to create mutex!");
+ }
+ MOZ_COUNT_CTOR(CrossProcessMutex);
+}
+
+CrossProcessMutex::CrossProcessMutex(CrossProcessMutexHandle aHandle)
+{
+ DWORD flags;
+ if (!::GetHandleInformation(aHandle, &flags)) {
+ NS_RUNTIMEABORT("Attempt to construct a mutex from an invalid handle!");
+ }
+ mMutex = aHandle;
+ MOZ_COUNT_CTOR(CrossProcessMutex);
+}
+
+CrossProcessMutex::~CrossProcessMutex()
+{
+ NS_ASSERTION(mMutex, "Improper construction of mutex or double free.");
+ ::CloseHandle(mMutex);
+ MOZ_COUNT_DTOR(CrossProcessMutex);
+}
+
+void
+CrossProcessMutex::Lock()
+{
+ NS_ASSERTION(mMutex, "Improper construction of mutex.");
+ ::WaitForSingleObject(mMutex, INFINITE);
+}
+
+void
+CrossProcessMutex::Unlock()
+{
+ NS_ASSERTION(mMutex, "Improper construction of mutex.");
+ ::ReleaseMutex(mMutex);
+}
+
+CrossProcessMutexHandle
+CrossProcessMutex::ShareToProcess(base::ProcessId aTargetPid)
+{
+ HANDLE newHandle;
+ bool succeeded = ipc::DuplicateHandle(mMutex, aTargetPid, &newHandle,
+ 0, DUPLICATE_SAME_ACCESS);
+
+ if (!succeeded) {
+ return nullptr;
+ }
+
+ return newHandle;
+}
+
+}
diff --git a/ipc/glue/Faulty.cpp b/ipc/glue/Faulty.cpp
new file mode 100644
index 000000000..5c326cd3f
--- /dev/null
+++ b/ipc/glue/Faulty.cpp
@@ -0,0 +1,654 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/Faulty.h"
+#include <cerrno>
+#include <prinrval.h>
+#include "nsXULAppAPI.h"
+#include "base/string_util.h"
+#include "chrome/common/ipc_message.h"
+#include "chrome/common/ipc_channel.h"
+#include "prenv.h"
+#include "mozilla/TypeTraits.h"
+#include <cmath>
+#include <climits>
+
+namespace mozilla {
+namespace ipc {
+
+const unsigned int Faulty::sDefaultProbability = Faulty::DefaultProbability();
+const bool Faulty::sIsLoggingEnabled = Faulty::Logging();
+
+/**
+ * RandomNumericValue generates negative and positive integrals.
+ */
+template <typename T>
+T RandomIntegral()
+{
+ static_assert(mozilla::IsIntegral<T>::value == true,
+ "T must be an integral type");
+ double r = static_cast<double>(random() % ((sizeof(T) * CHAR_BIT) + 1));
+ T x = static_cast<T>(pow(2.0, r)) - 1;
+ if (std::numeric_limits<T>::is_signed && random() % 2 == 0) {
+ return (x * -1) - 1;
+ }
+ return x;
+}
+
+/**
+ * RandomNumericLimit returns either the min or max limit of an arithmetic
+ * data type.
+ */
+template <typename T>
+T RandomNumericLimit() {
+ static_assert(mozilla::IsArithmetic<T>::value == true,
+ "T must be an arithmetic type");
+ return random() % 2 == 0 ? std::numeric_limits<T>::min()
+ : std::numeric_limits<T>::max();
+}
+
+/**
+ * RandomIntegerRange returns a random integral within a user defined range.
+ */
+template <typename T>
+T RandomIntegerRange(T min, T max)
+{
+ static_assert(mozilla::IsIntegral<T>::value == true,
+ "T must be an integral type");
+ MOZ_ASSERT(min < max);
+ return static_cast<T>(random() % (max - min) + min);
+}
+
+/**
+ * RandomFloatingPointRange returns a random floating-point number within a
+ * user defined range.
+ */
+template <typename T>
+T RandomFloatingPointRange(T min, T max)
+{
+ static_assert(mozilla::IsFloatingPoint<T>::value == true,
+ "T must be a floating point type");
+ MOZ_ASSERT(min < max);
+ T x = static_cast<T>(random()) / static_cast<T>(RAND_MAX);
+ return min + x * (max - min);
+}
+
+/**
+ * RandomFloatingPoint returns a random floating-point number.
+ */
+template <typename T>
+T RandomFloatingPoint()
+{
+ static_assert(mozilla::IsFloatingPoint<T>::value == true,
+ "T must be a floating point type");
+ int radix = RandomIntegerRange<int>(std::numeric_limits<T>::min_exponent,
+ std::numeric_limits<T>::max_exponent);
+ T x = static_cast<T>(pow(2.0, static_cast<double>(radix)));
+ return x * RandomFloatingPointRange<T>(1.0, 2.0);
+}
+
+/**
+ * FuzzIntegralType mutates an incercepted integral type of a pickled message.
+ */
+template <typename T>
+void FuzzIntegralType(T* v, bool largeValues)
+{
+ static_assert(mozilla::IsIntegral<T>::value == true,
+ "T must be an integral type");
+ switch (random() % 6) {
+ case 0:
+ if (largeValues) {
+ (*v) = RandomIntegral<T>();
+ break;
+ }
+ // Fall through
+ case 1:
+ if (largeValues) {
+ (*v) = RandomNumericLimit<T>();
+ break;
+ }
+ // Fall through
+ case 2:
+ if (largeValues) {
+ (*v) = RandomIntegerRange<T>(std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max());
+ break;
+ }
+ // Fall through
+ default:
+ switch(random() % 2) {
+ case 0:
+ // Prevent underflow
+ if (*v != std::numeric_limits<T>::min()) {
+ (*v)--;
+ break;
+ }
+ // Fall through
+ case 1:
+ // Prevent overflow
+ if (*v != std::numeric_limits<T>::max()) {
+ (*v)++;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * FuzzFloatingPointType mutates an incercepted floating-point type of a
+ * pickled message.
+ */
+template <typename T>
+void FuzzFloatingPointType(T* v, bool largeValues)
+{
+ static_assert(mozilla::IsFloatingPoint<T>::value == true,
+ "T must be a floating point type");
+ switch (random() % 6) {
+ case 0:
+ if (largeValues) {
+ (*v) = RandomNumericLimit<T>();
+ break;
+ }
+ // Fall through
+ case 1:
+ if (largeValues) {
+ (*v) = RandomFloatingPointRange<T>(std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max());
+ break;
+ }
+ // Fall through
+ default:
+ (*v) = RandomFloatingPoint<T>();
+ }
+}
+
+/**
+ * FuzzStringType mutates an incercepted string type of a pickled message.
+ */
+template <typename T>
+void FuzzStringType(T& v, const T& literal1, const T& literal2)
+{
+ switch (random() % 5) {
+ case 4:
+ v = v + v;
+ // Fall through
+ case 3:
+ v = v + v;
+ // Fall through
+ case 2:
+ v = v + v;
+ break;
+ case 1:
+ v += literal1;
+ break;
+ case 0:
+ v = literal2;
+ break;
+ }
+}
+
+
+Faulty::Faulty()
+ // Enables the strategy for fuzzing pipes.
+ : mFuzzPipes(!!PR_GetEnv("FAULTY_PIPE"))
+ // Enables the strategy for fuzzing pickled messages.
+ , mFuzzPickle(!!PR_GetEnv("FAULTY_PICKLE"))
+ // Uses very large values while fuzzing pickled messages.
+ // This may cause a high amount of malloc_abort() / NS_ABORT_OOM crashes.
+ , mUseLargeValues(!!PR_GetEnv("FAULTY_LARGE_VALUES"))
+ // Sets up our target process.
+ , mIsValidProcessType(IsValidProcessType())
+{
+ FAULTY_LOG("Initializing.");
+
+ const char* userSeed = PR_GetEnv("FAULTY_SEED");
+ unsigned long randomSeed = static_cast<unsigned long>(PR_IntervalNow());
+ if (userSeed) {
+ long n = std::strtol(userSeed, nullptr, 10);
+ if (n != 0) {
+ randomSeed = static_cast<unsigned long>(n);
+ }
+ }
+ srandom(randomSeed);
+
+ FAULTY_LOG("Fuzz probability = %u", sDefaultProbability);
+ FAULTY_LOG("Random seed = %lu", randomSeed);
+ FAULTY_LOG("Strategy: pickle = %s", mFuzzPickle ? "enabled" : "disabled");
+ FAULTY_LOG("Strategy: pipe = %s", mFuzzPipes ? "enabled" : "disabled");
+}
+
+// static
+bool
+Faulty::IsValidProcessType(void)
+{
+ bool isValidProcessType;
+ const bool targetChildren = !!PR_GetEnv("FAULTY_CHILDREN");
+ const bool targetParent = !!PR_GetEnv("FAULTY_PARENT");
+
+ if (targetChildren && !targetParent) {
+ // Fuzz every process type but not the content process.
+ isValidProcessType = XRE_GetProcessType() != GeckoProcessType_Content;
+ } else if (targetChildren && targetParent) {
+ // Fuzz every process type.
+ isValidProcessType = true;
+ } else {
+ // Fuzz the content process only.
+ isValidProcessType = XRE_GetProcessType() == GeckoProcessType_Content;
+ }
+
+ // Parent and children are different threads in the same process on
+ // desktop builds.
+ if (!isValidProcessType) {
+ FAULTY_LOG("Invalid process type for pid=%d", getpid());
+ }
+
+ return isValidProcessType;
+}
+
+// static
+unsigned int
+Faulty::DefaultProbability(void)
+{
+ // Defines the likelihood of fuzzing a message.
+ const char* probability = PR_GetEnv("FAULTY_PROBABILITY");
+ if (probability) {
+ long n = std::strtol(probability, nullptr, 10);
+ if (n != 0) {
+ return n;
+ }
+ }
+ return FAULTY_DEFAULT_PROBABILITY;
+}
+
+// static
+bool
+Faulty::Logging(void)
+{
+ // Enables logging of sendmsg() calls even in optimized builds.
+ return !!PR_GetEnv("FAULTY_ENABLE_LOGGING");
+}
+
+unsigned int
+Faulty::Random(unsigned int aMax)
+{
+ MOZ_ASSERT(aMax > 0);
+ return static_cast<unsigned int>(random() % aMax);
+}
+
+bool
+Faulty::GetChance(unsigned int aProbability)
+{
+ return Random(aProbability) == 0;
+}
+
+//
+// Strategy: Pipes
+//
+
+void
+Faulty::MaybeCollectAndClosePipe(int aPipe, unsigned int aProbability)
+{
+ if (!mFuzzPipes) {
+ return;
+ }
+
+ if (aPipe > -1) {
+ FAULTY_LOG("collecting pipe %d to bucket of pipes (count: %ld)",
+ aPipe, mFds.size());
+ mFds.insert(aPipe);
+ }
+
+ if (mFds.size() > 0 && GetChance(aProbability)) {
+ std::set<int>::iterator it(mFds.begin());
+ std::advance(it, Random(mFds.size()));
+ FAULTY_LOG("trying to close collected pipe: %d", *it);
+ errno = 0;
+ while ((close(*it) == -1 && (errno == EINTR))) {
+ ;
+ }
+ FAULTY_LOG("pipe status after attempt to close: %d", errno);
+ mFds.erase(it);
+ }
+}
+
+//
+// Strategy: Pickle
+//
+
+void
+Faulty::MutateBool(bool* aValue)
+{
+ *aValue = !(*aValue);
+}
+
+void
+Faulty::FuzzBool(bool* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ bool oldValue = *aValue;
+ MutateBool(aValue);
+ FAULTY_LOG("pickle field {bool} of value: %d changed to: %d",
+ (int)oldValue, (int)*aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateChar(char* aValue)
+{
+ FuzzIntegralType<char>(aValue, true);
+}
+
+void
+Faulty::FuzzChar(char* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ char oldValue = *aValue;
+ MutateChar(aValue);
+ FAULTY_LOG("pickle field {char} of value: %c changed to: %c",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateUChar(unsigned char* aValue)
+{
+ FuzzIntegralType<unsigned char>(aValue, true);
+}
+
+void
+Faulty::FuzzUChar(unsigned char* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ unsigned char oldValue = *aValue;
+ MutateUChar(aValue);
+ FAULTY_LOG("pickle field {unsigned char} of value: %u changed to: %u",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateInt16(int16_t* aValue)
+{
+ FuzzIntegralType<int16_t>(aValue, true);
+}
+
+void
+Faulty::FuzzInt16(int16_t* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ int16_t oldValue = *aValue;
+ MutateInt16(aValue);
+ FAULTY_LOG("pickle field {Int16} of value: %d changed to: %d",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateUInt16(uint16_t* aValue)
+{
+ FuzzIntegralType<uint16_t>(aValue, true);
+}
+
+void
+Faulty::FuzzUInt16(uint16_t* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ uint16_t oldValue = *aValue;
+ MutateUInt16(aValue);
+ FAULTY_LOG("pickle field {UInt16} of value: %d changed to: %d",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateInt(int* aValue)
+{
+ FuzzIntegralType<int>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzInt(int* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ int oldValue = *aValue;
+ MutateInt(aValue);
+ FAULTY_LOG("pickle field {int} of value: %d changed to: %d",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateUInt32(uint32_t* aValue)
+{
+ FuzzIntegralType<uint32_t>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzUInt32(uint32_t* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ uint32_t oldValue = *aValue;
+ MutateUInt32(aValue);
+ FAULTY_LOG("pickle field {UInt32} of value: %u changed to: %u",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateLong(long* aValue)
+{
+ FuzzIntegralType<long>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzLong(long* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ long oldValue = *aValue;
+ MutateLong(aValue);
+ FAULTY_LOG("pickle field {long} of value: %ld changed to: %ld",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateULong(unsigned long* aValue)
+{
+ FuzzIntegralType<unsigned long>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzULong(unsigned long* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ unsigned long oldValue = *aValue;
+ MutateULong(aValue);
+ FAULTY_LOG("pickle field {unsigned long} of value: %lu changed to: %lu",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateSize(size_t* aValue)
+{
+ FuzzIntegralType<size_t>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzSize(size_t* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ size_t oldValue = *aValue;
+ MutateSize(aValue);
+ FAULTY_LOG("pickle field {size_t} of value: %zu changed to: %zu",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateUInt64(uint64_t* aValue)
+{
+ FuzzIntegralType<uint64_t>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzUInt64(uint64_t* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ uint64_t oldValue = *aValue;
+ MutateUInt64(aValue);
+ FAULTY_LOG("pickle field {UInt64} of value: %llu changed to: %llu",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateInt64(int64_t* aValue)
+{
+ FuzzIntegralType<int64_t>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzInt64(int64_t* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ int64_t oldValue = *aValue;
+ MutateInt64(aValue);
+ FAULTY_LOG("pickle field {Int64} of value: %lld changed to: %lld",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateDouble(double* aValue)
+{
+ FuzzFloatingPointType<double>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzDouble(double* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ double oldValue = *aValue;
+ MutateDouble(aValue);
+ FAULTY_LOG("pickle field {double} of value: %f changed to: %f",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::MutateFloat(float* aValue)
+{
+ FuzzFloatingPointType<float>(aValue, mUseLargeValues);
+}
+
+void
+Faulty::FuzzFloat(float* aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ float oldValue = *aValue;
+ MutateFloat(aValue);
+ FAULTY_LOG("pickle field {float} of value: %f changed to: %f",
+ oldValue, *aValue);
+ }
+ }
+}
+
+void
+Faulty::FuzzString(std::string& aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ std::string oldValue = aValue;
+ FuzzStringType<std::string>(aValue, "xoferiF", std::string());
+ FAULTY_LOG("pickle field {string} of value: %s changed to: %s",
+ oldValue.c_str(), aValue.c_str());
+ }
+ }
+}
+
+void
+Faulty::FuzzWString(std::wstring& aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ std::wstring oldValue = aValue;
+ FAULTY_LOG("pickle field {wstring}");
+ FuzzStringType<std::wstring>(aValue, L"xoferiF", std::wstring());
+ }
+ }
+}
+
+void
+Faulty::FuzzString16(string16& aValue, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ string16 oldValue = aValue;
+ FAULTY_LOG("pickle field {string16}");
+ FuzzStringType<string16>(aValue,
+ string16(ASCIIToUTF16(std::string("xoferiF"))),
+ string16(ASCIIToUTF16(std::string())));
+ }
+ }
+}
+
+void
+Faulty::FuzzBytes(void* aData, int aLength, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ FAULTY_LOG("pickle field {bytes}");
+ // Too destructive. |WriteBytes| is used in many of the above data
+ // types as base function.
+ //FuzzData(static_cast<char*>(aData), aLength);
+ }
+ }
+}
+
+void
+Faulty::FuzzData(std::string& aValue, int aLength, unsigned int aProbability)
+{
+ if (mIsValidProcessType) {
+ if (mFuzzPickle && GetChance(aProbability)) {
+ FAULTY_LOG("pickle field {data}");
+ for (int i = 0; i < aLength; ++i) {
+ if (GetChance(aProbability)) {
+ FuzzIntegralType<char>(&aValue[i], true);
+ }
+ }
+ }
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/Faulty.h b/ipc/glue/Faulty.h
new file mode 100644
index 000000000..8afe86b06
--- /dev/null
+++ b/ipc/glue/Faulty.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_Faulty_h
+#define mozilla_ipc_Faulty_h
+
+#include <set>
+#include <string>
+#include "nsDebug.h"
+#include "base/string16.h"
+#include "base/singleton.h"
+
+#define FAULTY_DEFAULT_PROBABILITY 1000
+#define FAULTY_LOG(fmt, args...) \
+ if (mozilla::ipc::Faulty::IsLoggingEnabled()) { \
+ printf_stderr("[Faulty] " fmt "\n", ## args); \
+ }
+
+namespace IPC {
+ // Needed for blacklisting messages.
+ class Message;
+}
+
+namespace mozilla {
+namespace ipc {
+
+class Faulty
+{
+ public:
+ // Used as a default argument for the Fuzz|datatype| methods.
+ static const unsigned int sDefaultProbability;
+
+ static unsigned int DefaultProbability(void);
+ static bool Logging(void);
+ static bool IsLoggingEnabled(void) { return sIsLoggingEnabled; }
+
+ void FuzzBool(bool* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzChar(char* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzUChar(unsigned char* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzInt16(int16_t* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzUInt16(uint16_t* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzInt(int* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzUInt32(uint32_t* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzLong(long* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzULong(unsigned long* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzInt64(int64_t* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzUInt64(uint64_t* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzSize(size_t* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzFloat(float* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzDouble(double* aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzString(std::string& aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzWString(std::wstring& aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzString16(string16& aValue, unsigned int aProbability=sDefaultProbability);
+ void FuzzData(std::string& aData, int aLength, unsigned int aProbability=sDefaultProbability);
+ void FuzzBytes(void* aData, int aLength, unsigned int aProbability=sDefaultProbability);
+
+ void MaybeCollectAndClosePipe(int aPipe, unsigned int aProbability=sDefaultProbability);
+
+ private:
+ std::set<int> mFds;
+
+ const bool mFuzzPipes;
+ const bool mFuzzPickle;
+ const bool mUseLargeValues;
+ const bool mIsValidProcessType;
+
+ static const bool sIsLoggingEnabled;
+
+ Faulty();
+ friend struct DefaultSingletonTraits<Faulty>;
+ DISALLOW_EVIL_CONSTRUCTORS(Faulty);
+
+ static bool IsValidProcessType(void);
+
+ unsigned int Random(unsigned int aMax);
+ bool GetChance(unsigned int aProbability);
+
+ void MutateBool(bool* aValue);
+ void MutateChar(char* aValue);
+ void MutateUChar(unsigned char* aValue);
+ void MutateInt16(int16_t* aValue);
+ void MutateUInt16(uint16_t* aValue);
+ void MutateInt(int* aValue);
+ void MutateUInt32(uint32_t* aValue);
+ void MutateLong(long* aValue);
+ void MutateULong(unsigned long* aValue);
+ void MutateInt64(int64_t* aValue);
+ void MutateUInt64(uint64_t* aValue);
+ void MutateSize(size_t* aValue);
+ void MutateFloat(float* aValue);
+ void MutateDouble(double* aValue);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif
diff --git a/ipc/glue/FileDescriptor.cpp b/ipc/glue/FileDescriptor.cpp
new file mode 100644
index 000000000..1a8743d86
--- /dev/null
+++ b/ipc/glue/FileDescriptor.cpp
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "FileDescriptor.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+#include "nsDebug.h"
+
+#ifdef XP_WIN
+
+#include <windows.h>
+#include "ProtocolUtils.h"
+#define INVALID_HANDLE INVALID_HANDLE_VALUE
+
+#else // XP_WIN
+
+#include <unistd.h>
+
+#ifndef OS_POSIX
+#define OS_POSIX
+#endif
+
+#include "base/eintr_wrapper.h"
+#define INVALID_HANDLE -1
+
+#endif // XP_WIN
+
+using mozilla::ipc::FileDescriptor;
+
+FileDescriptor::FileDescriptor()
+ : mHandle(INVALID_HANDLE)
+{
+}
+
+FileDescriptor::FileDescriptor(const FileDescriptor& aOther)
+ : mHandle(INVALID_HANDLE)
+{
+ Assign(aOther);
+}
+
+FileDescriptor::FileDescriptor(FileDescriptor&& aOther)
+ : mHandle(INVALID_HANDLE)
+{
+ *this = mozilla::Move(aOther);
+}
+
+FileDescriptor::FileDescriptor(PlatformHandleType aHandle)
+ : mHandle(INVALID_HANDLE)
+{
+ mHandle = Clone(aHandle);
+}
+
+FileDescriptor::FileDescriptor(const IPDLPrivate&, const PickleType& aPickle)
+ : mHandle(INVALID_HANDLE)
+{
+#ifdef XP_WIN
+ mHandle = aPickle;
+#else
+ mHandle = aPickle.fd;
+#endif
+}
+
+FileDescriptor::~FileDescriptor()
+{
+ Close();
+}
+
+FileDescriptor&
+FileDescriptor::operator=(const FileDescriptor& aOther)
+{
+ if (this != &aOther) {
+ Assign(aOther);
+ }
+ return *this;
+}
+
+FileDescriptor&
+FileDescriptor::operator=(FileDescriptor&& aOther)
+{
+ if (this != &aOther) {
+ Close();
+ mHandle = aOther.mHandle;
+ aOther.mHandle = INVALID_HANDLE;
+ }
+ return *this;
+}
+
+bool
+FileDescriptor::IsValid() const
+{
+ return IsValid(mHandle);
+}
+
+void
+FileDescriptor::Assign(const FileDescriptor& aOther)
+{
+ Close();
+ mHandle = Clone(aOther.mHandle);
+}
+
+void
+FileDescriptor::Close()
+{
+ Close(mHandle);
+ mHandle = INVALID_HANDLE;
+}
+
+FileDescriptor::PickleType
+FileDescriptor::ShareTo(const FileDescriptor::IPDLPrivate&,
+ FileDescriptor::ProcessId aTargetPid) const
+{
+ PlatformHandleType newHandle;
+#ifdef XP_WIN
+ if (IsValid()) {
+ if (mozilla::ipc::DuplicateHandle(mHandle, aTargetPid, &newHandle, 0,
+ DUPLICATE_SAME_ACCESS)) {
+ return newHandle;
+ }
+ NS_WARNING("Failed to duplicate file handle for other process!");
+ }
+ return INVALID_HANDLE;
+#else // XP_WIN
+ if (IsValid()) {
+ newHandle = dup(mHandle);
+ if (IsValid(newHandle)) {
+ return base::FileDescriptor(newHandle, /* auto_close */ true);
+ }
+ NS_WARNING("Failed to duplicate file handle for other process!");
+ }
+ return base::FileDescriptor();
+#endif
+
+ MOZ_CRASH("Must not get here!");
+}
+
+FileDescriptor::UniquePlatformHandle
+FileDescriptor::ClonePlatformHandle() const
+{
+ return UniquePlatformHandle(Clone(mHandle));
+}
+
+bool
+FileDescriptor::operator==(const FileDescriptor& aOther) const
+{
+ return mHandle == aOther.mHandle;
+}
+
+// static
+bool
+FileDescriptor::IsValid(PlatformHandleType aHandle)
+{
+ return aHandle != INVALID_HANDLE;
+}
+
+// static
+FileDescriptor::PlatformHandleType
+FileDescriptor::Clone(PlatformHandleType aHandle)
+{
+ if (!IsValid(aHandle)) {
+ return INVALID_HANDLE;
+ }
+ FileDescriptor::PlatformHandleType newHandle;
+#ifdef XP_WIN
+ if (::DuplicateHandle(GetCurrentProcess(), aHandle, GetCurrentProcess(),
+ &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+#else // XP_WIN
+ if ((newHandle = dup(aHandle)) != INVALID_HANDLE) {
+#endif
+ return newHandle;
+ }
+ NS_WARNING("Failed to duplicate file handle for current process!");
+ return INVALID_HANDLE;
+}
+
+// static
+void
+FileDescriptor::Close(PlatformHandleType aHandle)
+{
+ if (IsValid(aHandle)) {
+#ifdef XP_WIN
+ if (!CloseHandle(aHandle)) {
+ NS_WARNING("Failed to close file handle for current process!");
+ }
+#else // XP_WIN
+ HANDLE_EINTR(close(aHandle));
+#endif
+ }
+}
+
+FileDescriptor::PlatformHandleHelper::PlatformHandleHelper(FileDescriptor::PlatformHandleType aHandle)
+ :mHandle(aHandle)
+{
+}
+
+FileDescriptor::PlatformHandleHelper::PlatformHandleHelper(std::nullptr_t)
+ :mHandle(INVALID_HANDLE)
+{
+}
+
+bool
+FileDescriptor::PlatformHandleHelper::operator!=(std::nullptr_t) const
+{
+ return mHandle != INVALID_HANDLE;
+}
+
+FileDescriptor::PlatformHandleHelper::operator FileDescriptor::PlatformHandleType () const
+{
+ return mHandle;
+}
+
+#ifdef XP_WIN
+FileDescriptor::PlatformHandleHelper::operator std::intptr_t () const
+{
+ return reinterpret_cast<std::intptr_t>(mHandle);
+}
+#endif
+
+void
+FileDescriptor::PlatformHandleDeleter::operator()(FileDescriptor::PlatformHandleHelper aHelper)
+{
+ FileDescriptor::Close(aHelper);
+}
diff --git a/ipc/glue/FileDescriptor.h b/ipc/glue/FileDescriptor.h
new file mode 100644
index 000000000..b30c5db1a
--- /dev/null
+++ b/ipc/glue/FileDescriptor.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_FileDescriptor_h
+#define mozilla_ipc_FileDescriptor_h
+
+#include "base/basictypes.h"
+#include "base/process.h"
+#include "mozilla/UniquePtr.h"
+
+#ifdef XP_WIN
+// Need the HANDLE typedef.
+#include <winnt.h>
+#else
+#include "base/file_descriptor_posix.h"
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+// This class is used by IPDL to share file descriptors across processes. When
+// sending a FileDescriptor IPDL will first duplicate a platform-specific file
+// handle type ('PlatformHandleType') into a handle that is valid in the other
+// process. Then IPDL will convert the duplicated handle into a type suitable
+// for pickling ('PickleType') and then send that through the IPC pipe. In the
+// receiving process the pickled data is converted into a platform-specific file
+// handle and then returned to the receiver.
+//
+// To use this class add 'FileDescriptor' as an argument in the IPDL protocol
+// and then pass a file descriptor from C++ to the Call/Send method. The
+// Answer/Recv method will receive a FileDescriptor& on which PlatformHandle()
+// can be called to return the platform file handle.
+class FileDescriptor
+{
+public:
+ typedef base::ProcessId ProcessId;
+
+#ifdef XP_WIN
+ typedef HANDLE PlatformHandleType;
+ typedef HANDLE PickleType;
+#else
+ typedef int PlatformHandleType;
+ typedef base::FileDescriptor PickleType;
+#endif
+
+ struct PlatformHandleHelper
+ {
+ MOZ_IMPLICIT PlatformHandleHelper(PlatformHandleType aHandle);
+ MOZ_IMPLICIT PlatformHandleHelper(std::nullptr_t);
+ bool operator != (std::nullptr_t) const;
+ operator PlatformHandleType () const;
+#ifdef XP_WIN
+ operator std::intptr_t () const;
+#endif
+ private:
+ PlatformHandleType mHandle;
+ };
+ struct PlatformHandleDeleter
+ {
+ typedef PlatformHandleHelper pointer;
+ void operator () (PlatformHandleHelper aHelper);
+ };
+ typedef UniquePtr<PlatformHandleType, PlatformHandleDeleter> UniquePlatformHandle;
+
+ // This should only ever be created by IPDL.
+ struct IPDLPrivate
+ {};
+
+ // Represents an invalid handle.
+ FileDescriptor();
+
+ // Copy constructor will duplicate a new handle.
+ FileDescriptor(const FileDescriptor& aOther);
+
+ FileDescriptor(FileDescriptor&& aOther);
+
+ // This constructor will duplicate a new handle.
+ // The caller still have to close aHandle.
+ explicit FileDescriptor(PlatformHandleType aHandle);
+
+ // This constructor WILL NOT duplicate the handle.
+ // FileDescriptor takes the ownership from IPC message.
+ FileDescriptor(const IPDLPrivate&, const PickleType& aPickle);
+
+ ~FileDescriptor();
+
+ FileDescriptor&
+ operator=(const FileDescriptor& aOther);
+
+ FileDescriptor&
+ operator=(FileDescriptor&& aOther);
+
+ // Performs platform-specific actions to duplicate mHandle in the other
+ // process (e.g. dup() on POSIX, DuplicateHandle() on Windows). Returns a
+ // pickled value that can be passed to the other process via IPC.
+ PickleType
+ ShareTo(const IPDLPrivate&, ProcessId aTargetPid) const;
+
+ // Tests mHandle against a well-known invalid platform-specific file handle
+ // (e.g. -1 on POSIX, INVALID_HANDLE_VALUE on Windows).
+ bool
+ IsValid() const;
+
+ // Returns a duplicated handle, it is caller's responsibility to close the
+ // handle.
+ UniquePlatformHandle
+ ClonePlatformHandle() const;
+
+ // Only used in nsTArray.
+ bool
+ operator==(const FileDescriptor& aOther) const;
+
+private:
+ friend struct PlatformHandleTrait;
+
+ void
+ Assign(const FileDescriptor& aOther);
+
+ void
+ Close();
+
+ static bool
+ IsValid(PlatformHandleType aHandle);
+
+ static PlatformHandleType
+ Clone(PlatformHandleType aHandle);
+
+ static void
+ Close(PlatformHandleType aHandle);
+
+ PlatformHandleType mHandle;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_FileDescriptor_h
diff --git a/ipc/glue/FileDescriptorSetChild.cpp b/ipc/glue/FileDescriptorSetChild.cpp
new file mode 100644
index 000000000..95fcbcf19
--- /dev/null
+++ b/ipc/glue/FileDescriptorSetChild.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileDescriptorSetChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+FileDescriptorSetChild::FileDescriptorSetChild(
+ const FileDescriptor& aFileDescriptor)
+{
+ mFileDescriptors.AppendElement(aFileDescriptor);
+}
+
+FileDescriptorSetChild::~FileDescriptorSetChild()
+{
+ MOZ_ASSERT(mFileDescriptors.IsEmpty());
+}
+
+void
+FileDescriptorSetChild::ForgetFileDescriptors(
+ nsTArray<FileDescriptor>& aFileDescriptors)
+{
+ aFileDescriptors.Clear();
+ mFileDescriptors.SwapElements(aFileDescriptors);
+}
+
+bool
+FileDescriptorSetChild::RecvAddFileDescriptor(
+ const FileDescriptor& aFileDescriptor)
+{
+ mFileDescriptors.AppendElement(aFileDescriptor);
+ return true;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/FileDescriptorSetChild.h b/ipc/glue/FileDescriptorSetChild.h
new file mode 100644
index 000000000..5c334a8f7
--- /dev/null
+++ b/ipc/glue/FileDescriptorSetChild.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_FileDescriptorSetChild_h__
+#define mozilla_ipc_FileDescriptorSetChild_h__
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/PFileDescriptorSetChild.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+namespace dom {
+
+class nsIContentChild;
+
+} // namespace dom
+
+namespace ipc {
+
+class BackgroundChildImpl;
+class FileDescriptor;
+
+class FileDescriptorSetChild final
+ : public PFileDescriptorSetChild
+{
+ friend class BackgroundChildImpl;
+ friend class mozilla::dom::nsIContentChild;
+
+ nsTArray<FileDescriptor> mFileDescriptors;
+
+public:
+ void
+ ForgetFileDescriptors(nsTArray<FileDescriptor>& aFileDescriptors);
+
+private:
+ explicit FileDescriptorSetChild(const FileDescriptor& aFileDescriptor);
+ ~FileDescriptorSetChild();
+
+ virtual bool
+ RecvAddFileDescriptor(const FileDescriptor& aFileDescriptor) override;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_FileDescriptorSetChild_h__
diff --git a/ipc/glue/FileDescriptorSetParent.cpp b/ipc/glue/FileDescriptorSetParent.cpp
new file mode 100644
index 000000000..685c433f5
--- /dev/null
+++ b/ipc/glue/FileDescriptorSetParent.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileDescriptorSetParent.h"
+
+namespace mozilla {
+namespace ipc {
+
+FileDescriptorSetParent::FileDescriptorSetParent(
+ const FileDescriptor& aFileDescriptor)
+{
+ mFileDescriptors.AppendElement(aFileDescriptor);
+}
+
+FileDescriptorSetParent::~FileDescriptorSetParent()
+{
+}
+
+void
+FileDescriptorSetParent::ForgetFileDescriptors(
+ nsTArray<FileDescriptor>& aFileDescriptors)
+{
+ aFileDescriptors.Clear();
+ mFileDescriptors.SwapElements(aFileDescriptors);
+}
+
+void
+FileDescriptorSetParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005157
+}
+
+bool
+FileDescriptorSetParent::RecvAddFileDescriptor(
+ const FileDescriptor& aFileDescriptor)
+{
+ mFileDescriptors.AppendElement(aFileDescriptor);
+ return true;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/FileDescriptorSetParent.h b/ipc/glue/FileDescriptorSetParent.h
new file mode 100644
index 000000000..58f254e47
--- /dev/null
+++ b/ipc/glue/FileDescriptorSetParent.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_FileDescriptorSetParent_h__
+#define mozilla_ipc_FileDescriptorSetParent_h__
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/PFileDescriptorSetParent.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+namespace dom {
+
+class nsIContentParent;
+
+} // namespace dom
+
+namespace ipc {
+
+class BackgroundParentImpl;
+class FileDescriptor;
+
+class FileDescriptorSetParent final
+ : public PFileDescriptorSetParent
+{
+ friend class BackgroundParentImpl;
+ friend class mozilla::dom::nsIContentParent;
+
+ nsTArray<FileDescriptor> mFileDescriptors;
+
+public:
+ void
+ ForgetFileDescriptors(nsTArray<FileDescriptor>& aFileDescriptors);
+
+private:
+ explicit FileDescriptorSetParent(const FileDescriptor& aFileDescriptor);
+ ~FileDescriptorSetParent();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool
+ RecvAddFileDescriptor(const FileDescriptor& aFileDescriptor) override;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_FileDescriptorSetParent_h__
diff --git a/ipc/glue/FileDescriptorUtils.cpp b/ipc/glue/FileDescriptorUtils.cpp
new file mode 100644
index 000000000..e30bc97d8
--- /dev/null
+++ b/ipc/glue/FileDescriptorUtils.cpp
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "FileDescriptorUtils.h"
+
+#include "nsIEventTarget.h"
+
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "prio.h"
+#include "private/pprio.h"
+
+#include <errno.h>
+#ifdef XP_WIN
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+using mozilla::ipc::CloseFileRunnable;
+
+#ifdef DEBUG
+
+CloseFileRunnable::CloseFileRunnable(const FileDescriptor& aFileDescriptor)
+: mFileDescriptor(aFileDescriptor)
+{
+ MOZ_ASSERT(aFileDescriptor.IsValid());
+}
+
+#endif // DEBUG
+
+CloseFileRunnable::~CloseFileRunnable()
+{
+ if (mFileDescriptor.IsValid()) {
+ // It's probably safer to take the main thread IO hit here rather than leak
+ // the file descriptor.
+ CloseFile();
+ }
+}
+
+NS_IMPL_ISUPPORTS(CloseFileRunnable, nsIRunnable)
+
+void
+CloseFileRunnable::Dispatch()
+{
+ nsCOMPtr<nsIEventTarget> eventTarget =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+ NS_ENSURE_TRUE_VOID(eventTarget);
+
+ nsresult rv = eventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS_VOID(rv);
+}
+
+void
+CloseFileRunnable::CloseFile()
+{
+ // It's possible for this to happen on the main thread if the dispatch to the
+ // stream service fails so we can't assert the thread on which we're running.
+ mFileDescriptor = FileDescriptor();
+}
+
+NS_IMETHODIMP
+CloseFileRunnable::Run()
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+
+ CloseFile();
+ return NS_OK;
+}
+
+namespace mozilla {
+namespace ipc {
+
+FILE*
+FileDescriptorToFILE(const FileDescriptor& aDesc,
+ const char* aOpenMode)
+{
+ if (!aDesc.IsValid()) {
+ errno = EBADF;
+ return nullptr;
+ }
+ auto handle = aDesc.ClonePlatformHandle();
+#ifdef XP_WIN
+ int fd = _open_osfhandle(static_cast<intptr_t>(handle.get()), 0);
+ if (fd == -1) {
+ return nullptr;
+ }
+ Unused << handle.release();
+#else
+ int fd = handle.release();
+#endif
+ FILE* file = fdopen(fd, aOpenMode);
+ if (!file) {
+ int saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ }
+ return file;
+}
+
+FileDescriptor
+FILEToFileDescriptor(FILE* aStream)
+{
+ if (!aStream) {
+ errno = EBADF;
+ return FileDescriptor();
+ }
+#ifdef XP_WIN
+ int fd = _fileno(aStream);
+ if (fd == -1) {
+ return FileDescriptor();
+ }
+ return FileDescriptor(reinterpret_cast<HANDLE>(_get_osfhandle(fd)));
+#else
+ return FileDescriptor(fileno(aStream));
+#endif
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/FileDescriptorUtils.h b/ipc/glue/FileDescriptorUtils.h
new file mode 100644
index 000000000..34562c361
--- /dev/null
+++ b/ipc/glue/FileDescriptorUtils.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_ipc_FileDescriptorUtils_h
+#define mozilla_ipc_FileDescriptorUtils_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "nsIRunnable.h"
+#include <stdio.h>
+
+namespace mozilla {
+namespace ipc {
+
+// When Dispatch() is called (from main thread) this class arranges to close the
+// provided FileDescriptor on one of the socket transport service threads (to
+// avoid main thread I/O).
+class CloseFileRunnable final : public nsIRunnable
+{
+ typedef mozilla::ipc::FileDescriptor FileDescriptor;
+
+ FileDescriptor mFileDescriptor;
+
+public:
+ explicit CloseFileRunnable(const FileDescriptor& aFileDescriptor)
+#ifdef DEBUG
+ ;
+#else
+ : mFileDescriptor(aFileDescriptor)
+ { }
+#endif
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ void Dispatch();
+
+private:
+ ~CloseFileRunnable();
+
+ void CloseFile();
+};
+
+// On failure, FileDescriptorToFILE returns nullptr; on success,
+// returns duplicated FILE*.
+// This is meant for use with FileDescriptors received over IPC.
+FILE* FileDescriptorToFILE(const FileDescriptor& aDesc,
+ const char* aOpenMode);
+
+// FILEToFileDescriptor does not consume the given FILE*; it must be
+// fclose()d as normal, and this does not invalidate the returned
+// FileDescriptor.
+FileDescriptor FILEToFileDescriptor(FILE* aStream);
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_FileDescriptorUtils_h
diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp
new file mode 100644
index 000000000..48051472a
--- /dev/null
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -0,0 +1,1268 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "GeckoChildProcessHost.h"
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/process_watcher.h"
+#ifdef MOZ_WIDGET_COCOA
+#include "chrome/common/mach_ipc_mac.h"
+#include "base/rand_util.h"
+#include "nsILocalFileMac.h"
+#include "SharedMemoryBasic.h"
+#endif
+
+#include "MainThreadUtils.h"
+#include "mozilla/Sprintf.h"
+#include "prenv.h"
+#include "nsXPCOMPrivate.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsAppDirectoryServiceDefs.h"
+#endif
+
+#include "nsExceptionHandler.h"
+
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsPrintfCString.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Telemetry.h"
+#include "ProtocolUtils.h"
+#include <sys/stat.h>
+
+#ifdef XP_WIN
+#include "nsIWinTaskbar.h"
+#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
+
+#if defined(MOZ_SANDBOX)
+#include "mozilla/Preferences.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+#include "nsDirectoryServiceUtils.h"
+#endif
+#endif
+
+#include "nsTArray.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsNativeCharsetUtils.h"
+#include "nscore.h" // for NS_FREE_PERMANENT_DATA
+
+using mozilla::MonitorAutoLock;
+using mozilla::ipc::GeckoChildProcessHost;
+
+#ifdef ANDROID
+// Like its predecessor in nsExceptionHandler.cpp, this is
+// the magic number of a file descriptor remapping we must
+// preserve for the child process.
+static const int kMagicAndroidSystemPropFd = 5;
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
+static const bool kLowRightsSubprocesses =
+ // We currently only attempt to drop privileges on gonk, because we
+ // have no plugins or extensions to worry about breaking.
+#ifdef MOZ_WIDGET_GONK
+ true
+#else
+ false
+#endif
+ ;
+
+static bool
+ShouldHaveDirectoryService()
+{
+ return GeckoProcessType_Default == XRE_GetProcessType();
+}
+
+/*static*/
+base::ChildPrivileges
+GeckoChildProcessHost::DefaultChildPrivileges()
+{
+ return (kLowRightsSubprocesses ?
+ base::PRIVILEGES_UNPRIVILEGED : base::PRIVILEGES_INHERIT);
+}
+
+GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
+ ChildPrivileges aPrivileges)
+ : mProcessType(aProcessType),
+ mPrivileges(aPrivileges),
+ mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
+ mProcessState(CREATING_CHANNEL),
+#if defined(MOZ_SANDBOX) && defined(XP_WIN)
+ mEnableSandboxLogging(false),
+ mSandboxLevel(0),
+#endif
+ mChildProcessHandle(0)
+#if defined(MOZ_WIDGET_COCOA)
+ , mChildTask(MACH_PORT_NULL)
+#endif
+{
+ MOZ_COUNT_CTOR(GeckoChildProcessHost);
+}
+
+GeckoChildProcessHost::~GeckoChildProcessHost()
+
+{
+ AssertIOThread();
+
+ MOZ_COUNT_DTOR(GeckoChildProcessHost);
+
+ if (mChildProcessHandle != 0) {
+#if defined(MOZ_WIDGET_COCOA)
+ SharedMemoryBasic::CleanupForPid(mChildProcessHandle);
+#endif
+ ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
+#ifdef NS_FREE_PERMANENT_DATA
+ // If we're doing leak logging, shutdown can be slow.
+ , false // don't "force"
+#endif
+ );
+ }
+
+#if defined(MOZ_WIDGET_COCOA)
+ if (mChildTask != MACH_PORT_NULL)
+ mach_port_deallocate(mach_task_self(), mChildTask);
+#endif
+}
+
+//static
+auto
+GeckoChildProcessHost::GetPathToBinary(FilePath& exePath, GeckoProcessType processType) -> BinaryPathType
+{
+ if (sRunSelfAsContentProc &&
+ (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU)) {
+#if defined(OS_WIN)
+ wchar_t exePathBuf[MAXPATHLEN];
+ if (!::GetModuleFileNameW(nullptr, exePathBuf, MAXPATHLEN)) {
+ MOZ_CRASH("GetModuleFileNameW failed (FIXME)");
+ }
+ exePath = FilePath::FromWStringHack(exePathBuf);
+#elif defined(OS_POSIX)
+ exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
+#else
+# error Sorry; target OS not supported yet.
+#endif
+ return BinaryPathType::Self;
+ }
+
+ if (ShouldHaveDirectoryService()) {
+ MOZ_ASSERT(gGREBinPath);
+#ifdef OS_WIN
+ exePath = FilePath(char16ptr_t(gGREBinPath));
+#elif MOZ_WIDGET_COCOA
+ nsCOMPtr<nsIFile> childProcPath;
+ NS_NewLocalFile(nsDependentString(gGREBinPath), false,
+ getter_AddRefs(childProcPath));
+
+ // We need to use an App Bundle on OS X so that we can hide
+ // the dock icon. See Bug 557225.
+ childProcPath->AppendNative(NS_LITERAL_CSTRING("plugin-container.app"));
+ childProcPath->AppendNative(NS_LITERAL_CSTRING("Contents"));
+ childProcPath->AppendNative(NS_LITERAL_CSTRING("MacOS"));
+ nsCString tempCPath;
+ childProcPath->GetNativePath(tempCPath);
+ exePath = FilePath(tempCPath.get());
+#else
+ nsCString path;
+ NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path);
+ exePath = FilePath(path.get());
+#endif
+ }
+
+ if (exePath.empty()) {
+#ifdef OS_WIN
+ exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
+#else
+ exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
+#endif
+ exePath = exePath.DirName();
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ exePath = exePath.AppendASCII("lib");
+
+ // We must use the PIE binary on 5.0 and higher
+ const char* processName = mozilla::AndroidBridge::Bridge()->GetAPIVersion() >= 21 ?
+ MOZ_CHILD_PROCESS_NAME_PIE : MOZ_CHILD_PROCESS_NAME;
+
+ exePath = exePath.AppendASCII(processName);
+#else
+ exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
+#endif
+
+ return BinaryPathType::PluginContainer;
+}
+
+#ifdef MOZ_WIDGET_COCOA
+class AutoCFTypeObject {
+public:
+ explicit AutoCFTypeObject(CFTypeRef object)
+ {
+ mObject = object;
+ }
+ ~AutoCFTypeObject()
+ {
+ ::CFRelease(mObject);
+ }
+private:
+ CFTypeRef mObject;
+};
+#endif
+
+nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32_t *result)
+{
+ *result = 0;
+
+#ifdef MOZ_WIDGET_COCOA
+ CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (const UInt8*)path,
+ strlen(path),
+ false);
+ if (!url) {
+ return NS_ERROR_FAILURE;
+ }
+ AutoCFTypeObject autoPluginContainerURL(url);
+
+ CFArrayRef pluginContainerArchs = ::CFBundleCopyExecutableArchitecturesForURL(url);
+ if (!pluginContainerArchs) {
+ return NS_ERROR_FAILURE;
+ }
+ AutoCFTypeObject autoPluginContainerArchs(pluginContainerArchs);
+
+ CFIndex pluginArchCount = ::CFArrayGetCount(pluginContainerArchs);
+ for (CFIndex i = 0; i < pluginArchCount; i++) {
+ CFNumberRef currentArch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(pluginContainerArchs, i));
+ int currentArchInt = 0;
+ if (!::CFNumberGetValue(currentArch, kCFNumberIntType, &currentArchInt)) {
+ continue;
+ }
+ switch (currentArchInt) {
+ case kCFBundleExecutableArchitectureI386:
+ *result |= base::PROCESS_ARCH_I386;
+ break;
+ case kCFBundleExecutableArchitectureX86_64:
+ *result |= base::PROCESS_ARCH_X86_64;
+ break;
+ case kCFBundleExecutableArchitecturePPC:
+ *result |= base::PROCESS_ARCH_PPC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (*result ? NS_OK : NS_ERROR_FAILURE);
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+uint32_t GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type)
+{
+#ifdef MOZ_WIDGET_COCOA
+ if (type == GeckoProcessType_Plugin) {
+
+ // Cache this, it shouldn't ever change.
+ static uint32_t pluginContainerArchs = 0;
+ if (pluginContainerArchs == 0) {
+ FilePath exePath;
+ GetPathToBinary(exePath, type);
+ nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs);
+ NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!");
+ if (NS_FAILED(rv) || pluginContainerArchs == 0) {
+ pluginContainerArchs = base::GetCurrentProcessArchitecture();
+ }
+ }
+ return pluginContainerArchs;
+ }
+#endif
+
+ return base::GetCurrentProcessArchitecture();
+}
+
+// We start the unique IDs at 1 so that 0 can be used to mean that
+// a component has no unique ID assigned to it.
+uint32_t GeckoChildProcessHost::sNextUniqueID = 1;
+
+/* static */
+uint32_t
+GeckoChildProcessHost::GetUniqueID()
+{
+ return sNextUniqueID++;
+}
+
+void
+GeckoChildProcessHost::PrepareLaunch()
+{
+#ifdef MOZ_CRASHREPORTER
+ if (CrashReporter::GetEnabled()) {
+ CrashReporter::OOPInit();
+ }
+#endif
+
+#ifdef XP_WIN
+ if (mProcessType == GeckoProcessType_Plugin) {
+ InitWindowsGroupID();
+ }
+
+#if defined(MOZ_CONTENT_SANDBOX)
+ // We need to get the pref here as the process is launched off main thread.
+ if (mProcessType == GeckoProcessType_Content) {
+ mSandboxLevel = Preferences::GetInt("security.sandbox.content.level");
+ mEnableSandboxLogging =
+ Preferences::GetBool("security.sandbox.windows.log");
+ }
+#endif
+
+#if defined(MOZ_SANDBOX)
+ // For other process types we can't rely on them being launched on main
+ // thread and they may not have access to prefs in the child process, so allow
+ // them to turn on logging via an environment variable.
+ mEnableSandboxLogging = mEnableSandboxLogging
+ || !!PR_GetEnv("MOZ_WIN_SANDBOX_LOGGING");
+#endif
+#endif
+}
+
+#ifdef XP_WIN
+void GeckoChildProcessHost::InitWindowsGroupID()
+{
+ // On Win7+, pass the application user model to the child, so it can
+ // register with it. This insures windows created by the container
+ // properly group with the parent app on the Win7 taskbar.
+ nsCOMPtr<nsIWinTaskbar> taskbarInfo =
+ do_GetService(NS_TASKBAR_CONTRACTID);
+ if (taskbarInfo) {
+ bool isSupported = false;
+ taskbarInfo->GetAvailable(&isSupported);
+ nsAutoString appId;
+ if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) {
+ mGroupId.Append(appId);
+ } else {
+ mGroupId.Assign('-');
+ }
+ }
+}
+#endif
+
+bool
+GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
+{
+ PrepareLaunch();
+
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+ NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI");
+
+ ioLoop->PostTask(NewNonOwningRunnableMethod
+ <std::vector<std::string>, base::ProcessArchitecture>
+ (this, &GeckoChildProcessHost::RunPerformAsyncLaunch,
+ aExtraOpts, arch));
+
+ return WaitUntilConnected(aTimeoutMs);
+}
+
+bool
+GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts,
+ base::ProcessArchitecture arch)
+{
+ PrepareLaunch();
+
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+
+ ioLoop->PostTask(NewNonOwningRunnableMethod
+ <std::vector<std::string>, base::ProcessArchitecture>
+ (this, &GeckoChildProcessHost::RunPerformAsyncLaunch,
+ aExtraOpts, arch));
+
+ // This may look like the sync launch wait, but we only delay as
+ // long as it takes to create the channel.
+ MonitorAutoLock lock(mMonitor);
+ while (mProcessState < CHANNEL_INITIALIZED) {
+ lock.Wait();
+ }
+
+ return true;
+}
+
+bool
+GeckoChildProcessHost::WaitUntilConnected(int32_t aTimeoutMs)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ // NB: this uses a different mechanism than the chromium parent
+ // class.
+ PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ?
+ PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
+
+ MonitorAutoLock lock(mMonitor);
+ PRIntervalTime waitStart = PR_IntervalNow();
+ PRIntervalTime current;
+
+ // We'll receive several notifications, we need to exit when we
+ // have either successfully launched or have timed out.
+ while (mProcessState != PROCESS_CONNECTED) {
+ // If there was an error then return it, don't wait out the timeout.
+ if (mProcessState == PROCESS_ERROR) {
+ break;
+ }
+
+ lock.Wait(timeoutTicks);
+
+ if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) {
+ current = PR_IntervalNow();
+ PRIntervalTime elapsed = current - waitStart;
+ if (elapsed > timeoutTicks) {
+ break;
+ }
+ timeoutTicks = timeoutTicks - elapsed;
+ waitStart = current;
+ }
+ }
+
+ return mProcessState == PROCESS_CONNECTED;
+}
+
+bool
+GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
+{
+ PrepareLaunch();
+
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+ ioLoop->PostTask(NewNonOwningRunnableMethod
+ <std::vector<std::string>, base::ProcessArchitecture>
+ (this, &GeckoChildProcessHost::RunPerformAsyncLaunch,
+ aExtraOpts, base::GetCurrentProcessArchitecture()));
+
+ MonitorAutoLock lock(mMonitor);
+ while (mProcessState < PROCESS_CREATED) {
+ lock.Wait();
+ }
+ MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle);
+
+ return mProcessState < PROCESS_ERROR;
+}
+
+void
+GeckoChildProcessHost::InitializeChannel()
+{
+ CreateChannel();
+
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = CHANNEL_INITIALIZED;
+ lock.Notify();
+}
+
+void
+GeckoChildProcessHost::Join()
+{
+ AssertIOThread();
+
+ if (!mChildProcessHandle) {
+ return;
+ }
+
+ // If this fails, there's nothing we can do.
+ base::KillProcess(mChildProcessHandle, 0, /*wait*/true);
+ SetAlreadyDead();
+}
+
+void
+GeckoChildProcessHost::SetAlreadyDead()
+{
+ if (mChildProcessHandle &&
+ mChildProcessHandle != kInvalidProcessHandle) {
+ base::CloseProcessHandle(mChildProcessHandle);
+ }
+
+ mChildProcessHandle = 0;
+}
+
+int32_t GeckoChildProcessHost::mChildCounter = 0;
+
+void
+GeckoChildProcessHost::SetChildLogName(const char* varName, const char* origLogName,
+ nsACString &buffer)
+{
+ // We currently have no portable way to launch child with environment
+ // different than parent. So temporarily change NSPR_LOG_FILE so child
+ // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
+ // startup, so it's 'safe' to play with the parent's environment this way.)
+ buffer.Assign(varName);
+ buffer.Append(origLogName);
+
+ // Append child-specific postfix to name
+ buffer.AppendLiteral(".child-");
+ buffer.AppendInt(mChildCounter);
+
+ // Passing temporary to PR_SetEnv is ok here if we keep the temporary
+ // for the time we launch the sub-process. It's copied to the new
+ // environment.
+ PR_SetEnv(buffer.BeginReading());
+}
+
+bool
+GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch)
+{
+ // If NSPR log files are not requested, we're done.
+ const char* origNSPRLogName = PR_GetEnv("NSPR_LOG_FILE");
+ const char* origMozLogName = PR_GetEnv("MOZ_LOG_FILE");
+ if (!origNSPRLogName && !origMozLogName) {
+ return PerformAsyncLaunchInternal(aExtraOpts, arch);
+ }
+
+ // - Note: this code is not called re-entrantly, nor are restoreOrig*LogName
+ // or mChildCounter touched by any other thread, so this is safe.
+ ++mChildCounter;
+
+ // Must keep these on the same stack where from we call PerformAsyncLaunchInternal
+ // so that PR_DuplicateEnvironment() still sees a valid memory.
+ nsAutoCString nsprLogName;
+ nsAutoCString mozLogName;
+
+ if (origNSPRLogName) {
+ if (mRestoreOrigNSPRLogName.IsEmpty()) {
+ mRestoreOrigNSPRLogName.AssignLiteral("NSPR_LOG_FILE=");
+ mRestoreOrigNSPRLogName.Append(origNSPRLogName);
+ }
+ SetChildLogName("NSPR_LOG_FILE=", origNSPRLogName, nsprLogName);
+ }
+ if (origMozLogName) {
+ if (mRestoreOrigMozLogName.IsEmpty()) {
+ mRestoreOrigMozLogName.AssignLiteral("MOZ_LOG_FILE=");
+ mRestoreOrigMozLogName.Append(origMozLogName);
+ }
+ SetChildLogName("MOZ_LOG_FILE=", origMozLogName, mozLogName);
+ }
+
+ bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch);
+
+ // Revert to original value
+ if (origNSPRLogName) {
+ PR_SetEnv(mRestoreOrigNSPRLogName.get());
+ }
+ if (origMozLogName) {
+ PR_SetEnv(mRestoreOrigMozLogName.get());
+ }
+
+ return retval;
+}
+
+bool
+GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts,
+ base::ProcessArchitecture aArch)
+{
+ InitializeChannel();
+
+ bool ok = PerformAsyncLaunch(aExtraOpts, aArch);
+ if (!ok) {
+ // WaitUntilConnected might be waiting for us to signal.
+ // If something failed let's set the error state and notify.
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = PROCESS_ERROR;
+ lock.Notify();
+ CHROMIUM_LOG(ERROR) << "Failed to launch " <<
+ XRE_ChildProcessTypeToString(mProcessType) << " subprocess";
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_LAUNCH_FAILURE,
+ nsDependentCString(XRE_ChildProcessTypeToString(mProcessType)));
+ }
+ return ok;
+}
+
+void
+#if defined(XP_WIN)
+AddAppDirToCommandLine(CommandLine& aCmdLine)
+#else
+AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
+#endif
+{
+ // Content processes need access to application resources, so pass
+ // the full application directory path to the child process.
+ if (ShouldHaveDirectoryService()) {
+ nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+ NS_ASSERTION(directoryService, "Expected XPCOM to be available");
+ if (directoryService) {
+ nsCOMPtr<nsIFile> appDir;
+ // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the
+ // current process dir.
+ nsresult rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(appDir));
+ if (NS_SUCCEEDED(rv)) {
+#if defined(XP_WIN)
+ nsString path;
+ MOZ_ALWAYS_SUCCEEDS(appDir->GetPath(path));
+ aCmdLine.AppendLooseValue(UTF8ToWide("-appdir"));
+ std::wstring wpath(path.get());
+ aCmdLine.AppendLooseValue(wpath);
+#else
+ nsAutoCString path;
+ MOZ_ALWAYS_SUCCEEDS(appDir->GetNativePath(path));
+ aCmdLine.push_back("-appdir");
+ aCmdLine.push_back(path.get());
+#endif
+ }
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+ // Full path to the profile dir
+ nsCOMPtr<nsIFile> profileDir;
+ rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(profileDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString path;
+ MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path));
+ aCmdLine.push_back("-profile");
+ aCmdLine.push_back(path.get());
+ }
+#endif
+ }
+ }
+}
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void
+MaybeAddNsprLogFileAccess(std::vector<std::wstring>& aAllowedFilesReadWrite)
+{
+ const char* nsprLogFileEnv = PR_GetEnv("NSPR_LOG_FILE");
+ if (!nsprLogFileEnv) {
+ return;
+ }
+
+ nsDependentCString nsprLogFilePath(nsprLogFileEnv);
+ nsCOMPtr<nsIFile> nsprLogFile;
+ nsresult rv = NS_NewNativeLocalFile(nsprLogFilePath, true,
+ getter_AddRefs(nsprLogFile));
+ if (NS_FAILED(rv)) {
+ // Not an absolute path, try it as a relative one.
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR,
+ getter_AddRefs(nsprLogFile));
+ if (NS_FAILED(rv) || !nsprLogFile) {
+ NS_WARNING("Failed to get current working directory");
+ return;
+ }
+
+ rv = nsprLogFile->AppendRelativeNativePath(nsprLogFilePath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ }
+
+ nsAutoString resolvedFilePath;
+ rv = nsprLogFile->GetPath(resolvedFilePath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // Update the environment variable as well as adding the rule, because the
+ // Chromium sandbox can only allow access to fully qualified file paths. This
+ // only affects the environment for the child process we're about to create,
+ // because this will get reset to the original value in PerformAsyncLaunch.
+ aAllowedFilesReadWrite.push_back(std::wstring(resolvedFilePath.get()));
+ nsAutoCString resolvedEnvVar("NSPR_LOG_FILE=");
+ AppendUTF16toUTF8(resolvedFilePath, resolvedEnvVar);
+ PR_SetEnv(resolvedEnvVar.get());
+}
+
+static void
+AddContentSandboxAllowedFiles(int32_t aSandboxLevel,
+ std::vector<std::wstring>& aAllowedFilesRead)
+{
+ if (aSandboxLevel < 1) {
+ return;
+ }
+
+ nsCOMPtr<nsIFile> binDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(binDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsAutoString binDirPath;
+ rv = binDir->GetPath(binDirPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // If bin directory is on a remote drive add read access.
+ wchar_t volPath[MAX_PATH];
+ if (!::GetVolumePathNameW(binDirPath.get(), volPath, MAX_PATH)) {
+ return;
+ }
+
+ if (::GetDriveTypeW(volPath) != DRIVE_REMOTE) {
+ return;
+ }
+
+ // Convert network share path to format for sandbox policy.
+ if (Substring(binDirPath, 0, 2).Equals(L"\\\\")) {
+ binDirPath.InsertLiteral(u"??\\UNC", 1);
+ }
+
+ binDirPath.AppendLiteral(u"\\*");
+
+ aAllowedFilesRead.push_back(std::wstring(binDirPath.get()));
+}
+#endif
+
+bool
+GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch)
+{
+ // We rely on the fact that InitializeChannel() has already been processed
+ // on the IO thread before this point is reached.
+ if (!GetChannel()) {
+ return false;
+ }
+
+ base::ProcessHandle process = 0;
+
+ // send the child the PID so that it can open a ProcessHandle back to us.
+ // probably don't want to do this in the long run
+ char pidstring[32];
+ SprintfLiteral(pidstring,"%d", base::Process::Current().pid());
+
+ const char* const childProcessType =
+ XRE_ChildProcessTypeToString(mProcessType);
+
+//--------------------------------------------------
+#if defined(OS_POSIX)
+ // For POSIX, we have to be extremely anal about *not* using
+ // std::wstring in code compiled with Mozilla's -fshort-wchar
+ // configuration, because chromium is compiled with -fno-short-wchar
+ // and passing wstrings from one config to the other is unsafe. So
+ // we split the logic here.
+
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
+ base::environment_map newEnvVars;
+ ChildPrivileges privs = mPrivileges;
+ if (privs == base::PRIVILEGES_DEFAULT) {
+ privs = DefaultChildPrivileges();
+ }
+
+#if defined(MOZ_WIDGET_GTK)
+ if (mProcessType == GeckoProcessType_Content) {
+ // disable IM module to avoid sandbox violation
+ newEnvVars["GTK_IM_MODULE"] = "gtk-im-context-simple";
+ }
+#endif
+
+ // XPCOM may not be initialized in some subprocesses. We don't want
+ // to initialize XPCOM just for the directory service, especially
+ // since LD_LIBRARY_PATH is already set correctly in subprocesses
+ // (meaning that we don't need to set that up in the environment).
+ if (ShouldHaveDirectoryService()) {
+ MOZ_ASSERT(gGREBinPath);
+ nsCString path;
+ NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path);
+# if defined(OS_LINUX) || defined(OS_BSD)
+# if defined(MOZ_WIDGET_ANDROID)
+ path += "/lib";
+# endif // MOZ_WIDGET_ANDROID
+ const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH");
+ nsCString new_ld_lib_path(path.get());
+
+# if (MOZ_WIDGET_GTK == 3)
+ if (mProcessType == GeckoProcessType_Plugin) {
+ new_ld_lib_path.Append("/gtk2:");
+ new_ld_lib_path.Append(path.get());
+ }
+#endif
+ if (ld_library_path && *ld_library_path) {
+ new_ld_lib_path.Append(':');
+ new_ld_lib_path.Append(ld_library_path);
+ }
+ newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
+
+# elif OS_MACOSX
+ newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
+ // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
+ // process, and has no effect on other subprocesses (the hooks in
+ // libplugin_child_interpose.dylib become noops). But currently it
+ // gets set when launching any kind of subprocess.
+ //
+ // Trigger "dyld interposing" for the dylib that contains
+ // plugin_child_interpose.mm. This allows us to hook OS calls in the
+ // plugin process (ones that don't work correctly in a background
+ // process). Don't break any other "dyld interposing" that has already
+ // been set up by whatever may have launched the browser.
+ const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES");
+ nsCString interpose;
+ if (prevInterpose && strlen(prevInterpose) > 0) {
+ interpose.Assign(prevInterpose);
+ interpose.Append(':');
+ }
+ interpose.Append(path.get());
+ interpose.AppendLiteral("/libplugin_child_interpose.dylib");
+ newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
+# endif // OS_LINUX
+ }
+#endif // OS_LINUX || OS_MACOSX
+
+ FilePath exePath;
+ BinaryPathType pathType = GetPathToBinary(exePath, mProcessType);
+
+#ifdef MOZ_WIDGET_ANDROID
+ // The java wrapper unpacks this for us but can't make it executable
+ chmod(exePath.value().c_str(), 0700);
+#endif // MOZ_WIDGET_ANDROID
+
+#ifdef ANDROID
+ // Remap the Android property workspace to a well-known int,
+ // and update the environment to reflect the new value for the
+ // child process.
+ const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE");
+ if (apws) {
+ int fd = atoi(apws);
+ mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd));
+
+ char buf[32];
+ char *szptr = strchr(apws, ',');
+
+ snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr);
+ newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf;
+ }
+#endif // ANDROID
+
+#ifdef MOZ_WIDGET_GONK
+ if (const char *ldPreloadPath = getenv("LD_PRELOAD")) {
+ newEnvVars["LD_PRELOAD"] = ldPreloadPath;
+ }
+#endif // MOZ_WIDGET_GONK
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ // Preload libmozsandbox.so so that sandbox-related interpositions
+ // can be defined there instead of in the executable.
+ // (This could be made conditional on intent to use sandboxing, but
+ // it's harmless for non-sandboxed processes.)
+ {
+ nsAutoCString preload;
+ // Prepend this, because people can and do preload libpthread.
+ // (See bug 1222500.)
+ preload.AssignLiteral("libmozsandbox.so");
+ if (const char* oldPreload = PR_GetEnv("LD_PRELOAD")) {
+ // Doesn't matter if oldPreload is ""; extra separators are ignored.
+ preload.Append(' ');
+ preload.Append(oldPreload);
+ }
+ // Explicitly construct the std::string to make it clear that this
+ // isn't retaining a pointer to the nsCString's buffer.
+ newEnvVars["LD_PRELOAD"] = std::string(preload.get());
+ }
+#endif
+
+ // remap the IPC socket fd to a well-known int, as the OS does for
+ // STDOUT_FILENO, for example
+ int srcChannelFd, dstChannelFd;
+ channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
+ mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
+
+ // no need for kProcessChannelID, the child process inherits the
+ // other end of the socketpair() from us
+
+ std::vector<std::string> childArgv;
+
+ childArgv.push_back(exePath.value());
+
+ if (pathType == BinaryPathType::Self) {
+ childArgv.push_back("-contentproc");
+ }
+
+ childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end());
+
+ if (Omnijar::IsInitialized()) {
+ // Make sure that child processes can find the omnijar
+ // See XRE_InitCommandLine in nsAppRunner.cpp
+ nsAutoCString path;
+ nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
+ if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
+ childArgv.push_back("-greomni");
+ childArgv.push_back(path.get());
+ }
+ file = Omnijar::GetPath(Omnijar::APP);
+ if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
+ childArgv.push_back("-appomni");
+ childArgv.push_back(path.get());
+ }
+ }
+
+ // Add the application directory path (-appdir path)
+ AddAppDirToCommandLine(childArgv);
+
+ childArgv.push_back(pidstring);
+
+#if defined(MOZ_CRASHREPORTER)
+# if defined(OS_LINUX) || defined(OS_BSD)
+ int childCrashFd, childCrashRemapFd;
+ if (!CrashReporter::CreateNotificationPipeForChild(
+ &childCrashFd, &childCrashRemapFd))
+ return false;
+ if (0 <= childCrashFd) {
+ mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
+ // "true" == crash reporting enabled
+ childArgv.push_back("true");
+ }
+ else {
+ // "false" == crash reporting disabled
+ childArgv.push_back("false");
+ }
+# elif defined(MOZ_WIDGET_COCOA)
+ childArgv.push_back(CrashReporter::GetChildNotificationPipe());
+# endif // OS_LINUX
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+ // Add a mach port to the command line so the child can communicate its
+ // 'task_t' back to the parent.
+ //
+ // Put a random number into the channel name, so that a compromised renderer
+ // can't pretend being the child that's forked off.
+ std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d",
+ base::RandInt(0, std::numeric_limits<int>::max()));
+ childArgv.push_back(mach_connection_name.c_str());
+#endif
+
+ childArgv.push_back(childProcessType);
+
+ base::LaunchApp(childArgv, mFileMap,
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
+ newEnvVars, privs,
+#endif
+ false, &process, arch);
+
+ // We're in the parent and the child was launched. Close the child FD in the
+ // parent as soon as possible, which will allow the parent to detect when the
+ // child closes its FD (either due to normal exit or due to crash).
+ GetChannel()->CloseClientFileDescriptor();
+
+#ifdef MOZ_WIDGET_COCOA
+ // Wait for the child process to send us its 'task_t' data.
+ const int kTimeoutMs = 10000;
+
+ MachReceiveMessage child_message;
+ ReceivePort parent_recv_port(mach_connection_name.c_str());
+ kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
+ CHROMIUM_LOG(ERROR) << "parent WaitForMessage() failed: " << errString;
+ return false;
+ }
+
+ task_t child_task = child_message.GetTranslatedPort(0);
+ if (child_task == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
+ return false;
+ }
+
+ if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
+ return false;
+ }
+ MachPortSender parent_sender(child_message.GetTranslatedPort(1));
+
+ if (child_message.GetTranslatedPort(2) == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(2) failed.";
+ }
+ MachPortSender* parent_recv_port_memory_ack = new MachPortSender(child_message.GetTranslatedPort(2));
+
+ if (child_message.GetTranslatedPort(3) == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(3) failed.";
+ }
+ MachPortSender* parent_send_port_memory = new MachPortSender(child_message.GetTranslatedPort(3));
+
+ MachSendMessage parent_message(/* id= */0);
+ if (!parent_message.AddDescriptor(MachMsgPortDescriptor(bootstrap_port))) {
+ CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
+ return false;
+ }
+
+ ReceivePort* parent_recv_port_memory = new ReceivePort();
+ if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_recv_port_memory->GetPort()))) {
+ CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_recv_port_memory->GetPort() << ") failed.";
+ return false;
+ }
+
+ ReceivePort* parent_send_port_memory_ack = new ReceivePort();
+ if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_send_port_memory_ack->GetPort()))) {
+ CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_send_port_memory_ack->GetPort() << ") failed.";
+ return false;
+ }
+
+ err = parent_sender.SendMessage(parent_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
+ CHROMIUM_LOG(ERROR) << "parent SendMessage() failed: " << errString;
+ return false;
+ }
+
+ SharedMemoryBasic::SetupMachMemory(process, parent_recv_port_memory, parent_recv_port_memory_ack,
+ parent_send_port_memory, parent_send_port_memory_ack, false);
+
+#endif
+
+//--------------------------------------------------
+#elif defined(OS_WIN)
+
+ FilePath exePath;
+ BinaryPathType pathType = GetPathToBinary(exePath, mProcessType);
+
+ CommandLine cmdLine(exePath.ToWStringHack());
+
+ if (pathType == BinaryPathType::Self) {
+ cmdLine.AppendLooseValue(UTF8ToWide("-contentproc"));
+ }
+
+ cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());
+
+ for (std::vector<std::string>::iterator it = aExtraOpts.begin();
+ it != aExtraOpts.end();
+ ++it) {
+ cmdLine.AppendLooseValue(UTF8ToWide(*it));
+ }
+
+ if (Omnijar::IsInitialized()) {
+ // Make sure the child process can find the omnijar
+ // See XRE_InitCommandLine in nsAppRunner.cpp
+ nsAutoString path;
+ nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
+ if (file && NS_SUCCEEDED(file->GetPath(path))) {
+ cmdLine.AppendLooseValue(UTF8ToWide("-greomni"));
+ cmdLine.AppendLooseValue(path.get());
+ }
+ file = Omnijar::GetPath(Omnijar::APP);
+ if (file && NS_SUCCEEDED(file->GetPath(path))) {
+ cmdLine.AppendLooseValue(UTF8ToWide("-appomni"));
+ cmdLine.AppendLooseValue(path.get());
+ }
+ }
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ bool shouldSandboxCurrentProcess = false;
+
+ // XXX: Bug 1124167: We should get rid of the process specific logic for
+ // sandboxing in this class at some point. Unfortunately it will take a bit
+ // of reorganizing so I don't think this patch is the right time.
+ switch (mProcessType) {
+ case GeckoProcessType_Content:
+#if defined(MOZ_CONTENT_SANDBOX)
+ if (mSandboxLevel > 0 &&
+ !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+ // For now we treat every failure as fatal in SetSecurityLevelForContentProcess
+ // and just crash there right away. Should this change in the future then we
+ // should also handle the error here.
+ mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel);
+ shouldSandboxCurrentProcess = true;
+ AddContentSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead);
+ }
+#endif // MOZ_CONTENT_SANDBOX
+ break;
+ case GeckoProcessType_Plugin:
+ if (mSandboxLevel > 0 &&
+ !PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
+ bool ok = mSandboxBroker.SetSecurityLevelForPluginProcess(mSandboxLevel);
+ if (!ok) {
+ return false;
+ }
+ shouldSandboxCurrentProcess = true;
+ }
+ break;
+ case GeckoProcessType_IPDLUnitTest:
+ // XXX: We don't sandbox this process type yet
+ break;
+ case GeckoProcessType_GMPlugin:
+ if (!PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) {
+ // The Widevine CDM on Windows can only load at USER_RESTRICTED,
+ // not at USER_LOCKDOWN. So look in the command line arguments
+ // to see if we're loading the path to the Widevine CDM, and if
+ // so use sandbox level USER_RESTRICTED instead of USER_LOCKDOWN.
+ bool isWidevine = std::any_of(aExtraOpts.begin(), aExtraOpts.end(),
+ [](const std::string arg) { return arg.find("gmp-widevinecdm") != std::string::npos; });
+ auto level = isWidevine ? SandboxBroker::Restricted : SandboxBroker::LockDown;
+ bool ok = mSandboxBroker.SetSecurityLevelForGMPlugin(level);
+ if (!ok) {
+ return false;
+ }
+ shouldSandboxCurrentProcess = true;
+ }
+ break;
+ case GeckoProcessType_GPU:
+ break;
+ case GeckoProcessType_Default:
+ default:
+ MOZ_CRASH("Bad process type in GeckoChildProcessHost");
+ break;
+ };
+
+ if (shouldSandboxCurrentProcess) {
+ MaybeAddNsprLogFileAccess(mAllowedFilesReadWrite);
+ for (auto it = mAllowedFilesRead.begin();
+ it != mAllowedFilesRead.end();
+ ++it) {
+ mSandboxBroker.AllowReadFile(it->c_str());
+ }
+
+ for (auto it = mAllowedFilesReadWrite.begin();
+ it != mAllowedFilesReadWrite.end();
+ ++it) {
+ mSandboxBroker.AllowReadWriteFile(it->c_str());
+ }
+
+ for (auto it = mAllowedDirectories.begin();
+ it != mAllowedDirectories.end();
+ ++it) {
+ mSandboxBroker.AllowDirectory(it->c_str());
+ }
+ }
+#endif // XP_WIN && MOZ_SANDBOX
+
+ // Add the application directory path (-appdir path)
+ AddAppDirToCommandLine(cmdLine);
+
+ // XXX Command line params past this point are expected to be at
+ // the end of the command line string, and in a specific order.
+ // See XRE_InitChildProcess in nsEmbedFunction.
+
+ // Win app model id
+ cmdLine.AppendLooseValue(mGroupId.get());
+
+ // Process id
+ cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
+
+#if defined(MOZ_CRASHREPORTER)
+ cmdLine.AppendLooseValue(
+ UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
+#endif
+
+ // Process type
+ cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ if (shouldSandboxCurrentProcess) {
+ if (mSandboxBroker.LaunchApp(cmdLine.program().c_str(),
+ cmdLine.command_line_string().c_str(),
+ mEnableSandboxLogging,
+ &process)) {
+ EnvironmentLog("MOZ_PROCESS_LOG").print(
+ "==> process %d launched child process %d (%S)\n",
+ base::GetCurrentProcId(), base::GetProcId(process),
+ cmdLine.command_line_string().c_str());
+ }
+ } else
+#endif
+ {
+ base::LaunchApp(cmdLine, false, false, &process);
+
+#ifdef MOZ_SANDBOX
+ // We need to be able to duplicate handles to some types of non-sandboxed
+ // child processes.
+ if (mProcessType == GeckoProcessType_Content ||
+ mProcessType == GeckoProcessType_GPU ||
+ mProcessType == GeckoProcessType_GMPlugin) {
+ if (!mSandboxBroker.AddTargetPeer(process)) {
+ NS_WARNING("Failed to add content process as target peer.");
+ }
+ }
+#endif
+ }
+
+#else
+# error Sorry
+#endif
+
+ if (!process) {
+ return false;
+ }
+ // NB: on OS X, we block much longer than we need to in order to
+ // reach this call, waiting for the child process's task_t. The
+ // best way to fix that is to refactor this file, hard.
+#if defined(MOZ_WIDGET_COCOA)
+ mChildTask = child_task;
+#endif
+
+ if (!OpenPrivilegedHandle(base::GetProcId(process))
+#ifdef XP_WIN
+ // If we failed in opening the process handle, try harder by duplicating
+ // one.
+ && !::DuplicateHandle(::GetCurrentProcess(), process,
+ ::GetCurrentProcess(), &mChildProcessHandle,
+ PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ |
+ SYNCHRONIZE,
+ FALSE, 0)
+#endif
+ ) {
+ NS_RUNTIMEABORT("cannot open handle to child process");
+ }
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = PROCESS_CREATED;
+ lock.Notify();
+
+ return true;
+}
+
+bool
+GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid)
+{
+ if (mChildProcessHandle) {
+ MOZ_ASSERT(aPid == base::GetProcId(mChildProcessHandle));
+ return true;
+ }
+
+ return base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle);
+}
+
+void
+GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid)
+{
+ if (!OpenPrivilegedHandle(peer_pid)) {
+ NS_RUNTIMEABORT("can't open handle to child process");
+ }
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = PROCESS_CONNECTED;
+ lock.Notify();
+}
+
+void
+GeckoChildProcessHost::OnMessageReceived(IPC::Message&& aMsg)
+{
+ // We never process messages ourself, just save them up for the next
+ // listener.
+ mQueue.push(Move(aMsg));
+}
+
+void
+GeckoChildProcessHost::OnChannelError()
+{
+ // Update the process state to an error state if we have a channel
+ // error before we're connected. This fixes certain failures,
+ // but does not address the full range of possible issues described
+ // in the FIXME comment below.
+ MonitorAutoLock lock(mMonitor);
+ if (mProcessState < PROCESS_CONNECTED) {
+ mProcessState = PROCESS_ERROR;
+ lock.Notify();
+ }
+ // FIXME/bug 773925: save up this error for the next listener.
+}
+
+void
+GeckoChildProcessHost::GetQueuedMessages(std::queue<IPC::Message>& queue)
+{
+ // If this is called off the IO thread, bad things will happen.
+ DCHECK(MessageLoopForIO::current());
+ swap(queue, mQueue);
+ // We expect the next listener to take over processing of our queue.
+}
+
+bool GeckoChildProcessHost::sRunSelfAsContentProc(false);
diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h
new file mode 100644
index 000000000..3d55564ac
--- /dev/null
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __IPC_GLUE_GECKOCHILDPROCESSHOST_H__
+#define __IPC_GLUE_GECKOCHILDPROCESSHOST_H__
+
+#include "base/file_path.h"
+#include "base/process_util.h"
+#include "base/waitable_event.h"
+#include "chrome/common/child_process_host.h"
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/StaticPtr.h"
+
+#include "nsCOMPtr.h"
+#include "nsXULAppAPI.h" // for GeckoProcessType
+#include "nsString.h"
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+#include "sandboxBroker.h"
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+class GeckoChildProcessHost : public ChildProcessHost
+{
+protected:
+ typedef mozilla::Monitor Monitor;
+ typedef std::vector<std::string> StringVector;
+
+public:
+ typedef base::ChildPrivileges ChildPrivileges;
+ typedef base::ProcessHandle ProcessHandle;
+
+ static ChildPrivileges DefaultChildPrivileges();
+
+ explicit GeckoChildProcessHost(GeckoProcessType aProcessType,
+ ChildPrivileges aPrivileges=base::PRIVILEGES_DEFAULT);
+
+ ~GeckoChildProcessHost();
+
+ static nsresult GetArchitecturesForBinary(const char *path, uint32_t *result);
+
+ static uint32_t GetSupportedArchitecturesForProcessType(GeckoProcessType type);
+
+ static uint32_t GetUniqueID();
+
+ // Block until the IPC channel for our subprocess is initialized,
+ // but no longer. The child process may or may not have been
+ // created when this method returns.
+ bool AsyncLaunch(StringVector aExtraOpts=StringVector(),
+ base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
+
+ virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0);
+
+ // Block until the IPC channel for our subprocess is initialized and
+ // the OS process is created. The subprocess may or may not have
+ // connected back to us when this method returns.
+ //
+ // NB: on POSIX, this method is relatively cheap, and doesn't
+ // require disk IO. On win32 however, it requires at least the
+ // analogue of stat(). This difference induces a semantic
+ // difference in this method: on POSIX, when we return, we know the
+ // subprocess has been created, but we don't know whether its
+ // executable image can be loaded. On win32, we do know that when
+ // we return. But we don't know if dynamic linking succeeded on
+ // either platform.
+ bool LaunchAndWaitForProcessHandle(StringVector aExtraOpts=StringVector());
+
+ // Block until the child process has been created and it connects to
+ // the IPC channel, meaning it's fully initialized. (Or until an
+ // error occurs.)
+ bool SyncLaunch(StringVector aExtraOpts=StringVector(),
+ int32_t timeoutMs=0,
+ base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
+
+ virtual bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
+ base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
+
+ virtual void OnChannelConnected(int32_t peer_pid);
+ virtual void OnMessageReceived(IPC::Message&& aMsg);
+ virtual void OnChannelError();
+ virtual void GetQueuedMessages(std::queue<IPC::Message>& queue);
+
+ virtual void InitializeChannel();
+
+ virtual bool CanShutdown() { return true; }
+
+ IPC::Channel* GetChannel() {
+ return channelp();
+ }
+
+ // Returns a "borrowed" handle to the child process - the handle returned
+ // by this function must not be closed by the caller.
+ ProcessHandle GetChildProcessHandle() {
+ return mChildProcessHandle;
+ }
+
+ GeckoProcessType GetProcessType() {
+ return mProcessType;
+ }
+
+#ifdef XP_MACOSX
+ task_t GetChildTask() {
+ return mChildTask;
+ }
+#endif
+
+ /**
+ * Must run on the IO thread. Cause the OS process to exit and
+ * ensure its OS resources are cleaned up.
+ */
+ void Join();
+
+ // For bug 943174: Skip the EnsureProcessTerminated call in the destructor.
+ void SetAlreadyDead();
+
+ static void EnableSameExecutableForContentProc() { sRunSelfAsContentProc = true; }
+
+protected:
+ GeckoProcessType mProcessType;
+ ChildPrivileges mPrivileges;
+ Monitor mMonitor;
+ FilePath mProcessPath;
+
+ // This value must be accessed while holding mMonitor.
+ enum {
+ // This object has been constructed, but the OS process has not
+ // yet.
+ CREATING_CHANNEL = 0,
+ // The IPC channel for our subprocess has been created, but the OS
+ // process has still not been created.
+ CHANNEL_INITIALIZED,
+ // The OS process has been created, but it hasn't yet connected to
+ // our IPC channel.
+ PROCESS_CREATED,
+ // The process is launched and connected to our IPC channel. All
+ // is well.
+ PROCESS_CONNECTED,
+ PROCESS_ERROR
+ } mProcessState;
+
+ static int32_t mChildCounter;
+
+ void PrepareLaunch();
+
+#ifdef XP_WIN
+ void InitWindowsGroupID();
+ nsString mGroupId;
+
+#ifdef MOZ_SANDBOX
+ SandboxBroker mSandboxBroker;
+ std::vector<std::wstring> mAllowedFilesRead;
+ std::vector<std::wstring> mAllowedFilesReadWrite;
+ std::vector<std::wstring> mAllowedDirectories;
+ bool mEnableSandboxLogging;
+ int32_t mSandboxLevel;
+#endif
+#endif // XP_WIN
+
+#if defined(OS_POSIX)
+ base::file_handle_mapping_vector mFileMap;
+#endif
+
+ ProcessHandle mChildProcessHandle;
+#if defined(OS_MACOSX)
+ task_t mChildTask;
+#endif
+
+ bool OpenPrivilegedHandle(base::ProcessId aPid);
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);
+
+ // Does the actual work for AsyncLaunch, on the IO thread.
+ bool PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts,
+ base::ProcessArchitecture arch);
+
+ bool RunPerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
+ base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
+
+ enum class BinaryPathType {
+ Self,
+ PluginContainer
+ };
+
+ static BinaryPathType GetPathToBinary(FilePath& exePath, GeckoProcessType processType);
+
+ // The buffer is passed to preserve its lifetime until we are done
+ // with launching the sub-process.
+ void SetChildLogName(const char* varName, const char* origLogName,
+ nsACString &buffer);
+
+ // In between launching the subprocess and handing off its IPC
+ // channel, there's a small window of time in which *we* might still
+ // be the channel listener, and receive messages. That's bad
+ // because we have no idea what to do with those messages. So queue
+ // them here until we hand off the eventual listener.
+ //
+ // FIXME/cjones: this strongly indicates bad design. Shame on us.
+ std::queue<IPC::Message> mQueue;
+
+ // Remember original env values so we can restore it (there is no other
+ // simple way how to change environment of a child process than to modify
+ // the current environment).
+ nsCString mRestoreOrigNSPRLogName;
+ nsCString mRestoreOrigMozLogName;
+
+ static uint32_t sNextUniqueID;
+
+ static bool sRunSelfAsContentProc;
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* __IPC_GLUE_GECKOCHILDPROCESSHOST_H__ */
diff --git a/ipc/glue/IOThreadChild.h b/ipc/glue/IOThreadChild.h
new file mode 100644
index 000000000..0a595ff3f
--- /dev/null
+++ b/ipc/glue/IOThreadChild.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_plugins_IOThreadChild_h
+#define dom_plugins_IOThreadChild_h 1
+
+#include "chrome/common/child_thread.h"
+
+namespace mozilla {
+namespace ipc {
+//-----------------------------------------------------------------------------
+
+// The IOThreadChild class represents a background thread where the
+// IPC IO MessageLoop lives.
+class IOThreadChild : public ChildThread {
+public:
+ IOThreadChild()
+ : ChildThread(base::Thread::Options(MessageLoop::TYPE_IO,
+ 0)) // stack size
+ { }
+
+ ~IOThreadChild()
+ { }
+
+ static MessageLoop* message_loop() {
+ return IOThreadChild::current()->Thread::message_loop();
+ }
+
+ // IOThreadChild owns the returned IPC::Channel.
+ static IPC::Channel* channel() {
+ return IOThreadChild::current()->ChildThread::channel();
+ }
+
+protected:
+ static IOThreadChild* current() {
+ return static_cast<IOThreadChild*>(ChildThread::current());
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(IOThreadChild);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // ifndef dom_plugins_IOThreadChild_h
diff --git a/ipc/glue/IPCMessageUtils.cpp b/ipc/glue/IPCMessageUtils.cpp
new file mode 100644
index 000000000..62af265ed
--- /dev/null
+++ b/ipc/glue/IPCMessageUtils.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "IPCMessageUtils.h"
+#include "mozilla/CheckedInt.h"
+
+namespace IPC {
+
+bool
+ByteLengthIsValid(uint32_t aNumElements, size_t aElementSize, int* aByteLength)
+{
+ auto length = mozilla::CheckedInt<int>(aNumElements) * aElementSize;
+ if (!length.isValid()) {
+ return false;
+ }
+ *aByteLength = length.value();
+ return true;
+}
+
+} // namespace IPC
diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h
new file mode 100644
index 000000000..094aa978a
--- /dev/null
+++ b/ipc/glue/IPCMessageUtils.h
@@ -0,0 +1,830 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __IPC_GLUE_IPCMESSAGEUTILS_H__
+#define __IPC_GLUE_IPCMESSAGEUTILS_H__
+
+#include "base/process_util.h"
+#include "chrome/common/ipc_message_utils.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/net/WebSocketFrame.h"
+#include "mozilla/TimeStamp.h"
+#ifdef XP_WIN
+#include "mozilla/TimeStamp_windows.h"
+#endif
+#include "mozilla/TypeTraits.h"
+#include "mozilla/IntegerTypeTraits.h"
+
+#include <stdint.h>
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+#include "nsID.h"
+#include "nsIWidget.h"
+#include "nsMemory.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "js/StructuredClone.h"
+#include "nsCSSPropertyID.h"
+
+#ifdef _MSC_VER
+#pragma warning( disable : 4800 )
+#endif
+
+#if !defined(OS_POSIX)
+// This condition must be kept in sync with the one in
+// ipc_message_utils.h, but this dummy definition of
+// base::FileDescriptor acts as a static assert that we only get one
+// def or the other (or neither, in which case code using
+// FileDescriptor fails to build)
+namespace base { struct FileDescriptor { }; }
+#endif
+
+namespace mozilla {
+
+// This is a cross-platform approximation to HANDLE, which we expect
+// to be typedef'd to void* or thereabouts.
+typedef uintptr_t WindowsHandle;
+
+// XXX there are out of place and might be generally useful. Could
+// move to nscore.h or something.
+struct void_t {
+ bool operator==(const void_t&) const { return true; }
+};
+struct null_t {
+ bool operator==(const null_t&) const { return true; }
+};
+
+struct SerializedStructuredCloneBuffer final
+{
+ SerializedStructuredCloneBuffer&
+ operator=(const SerializedStructuredCloneBuffer& aOther)
+ {
+ data.Clear();
+ auto iter = aOther.data.Iter();
+ while (!iter.Done()) {
+ data.WriteBytes(iter.Data(), iter.RemainingInSegment());
+ iter.Advance(aOther.data, iter.RemainingInSegment());
+ }
+ return *this;
+ }
+
+ bool
+ operator==(const SerializedStructuredCloneBuffer& aOther) const
+ {
+ // The copy assignment operator and the equality operator are
+ // needed by the IPDL generated code. We relied on the copy
+ // assignment operator at some places but we never use the
+ // equality operator.
+ return false;
+ }
+
+ JSStructuredCloneData data;
+};
+
+} // namespace mozilla
+
+namespace IPC {
+
+/**
+ * Maximum size, in bytes, of a single IPC message.
+ */
+static const uint32_t MAX_MESSAGE_SIZE = 65536;
+
+/**
+ * Generic enum serializer.
+ *
+ * Consider using the specializations below, such as ContiguousEnumSerializer.
+ *
+ * This is a generic serializer for any enum type used in IPDL.
+ * Programmers can define ParamTraits<E> for enum type E by deriving
+ * EnumSerializer<E, MyEnumValidator> where MyEnumValidator is a struct
+ * that has to define a static IsLegalValue function returning whether
+ * a given value is a legal value of the enum type at hand.
+ *
+ * \sa https://developer.mozilla.org/en/IPDL/Type_Serialization
+ */
+template <typename E, typename EnumValidator>
+struct EnumSerializer {
+ typedef E paramType;
+ typedef typename mozilla::UnsignedStdintTypeForSize<sizeof(paramType)>::Type
+ uintParamType;
+
+ static void Write(Message* aMsg, const paramType& aValue) {
+ MOZ_ASSERT(EnumValidator::IsLegalValue(aValue));
+ WriteParam(aMsg, uintParamType(aValue));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+ uintParamType value;
+ if (!ReadParam(aMsg, aIter, &value)) {
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCReadErrorReason"),
+ NS_LITERAL_CSTRING("Bad iter"));
+#endif
+ return false;
+ } else if (!EnumValidator::IsLegalValue(paramType(value))) {
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCReadErrorReason"),
+ NS_LITERAL_CSTRING("Illegal value"));
+#endif
+ return false;
+ }
+ *aResult = paramType(value);
+ return true;
+ }
+};
+
+template <typename E,
+ E MinLegal,
+ E HighBound>
+class ContiguousEnumValidator
+{
+ // Silence overzealous -Wtype-limits bug in GCC fixed in GCC 4.8:
+ // "comparison of unsigned expression >= 0 is always true"
+ // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11856
+ template <typename T>
+ static bool IsLessThanOrEqual(T a, T b) { return a <= b; }
+
+public:
+ static bool IsLegalValue(E e)
+ {
+ return IsLessThanOrEqual(MinLegal, e) && e < HighBound;
+ }
+};
+
+template <typename E,
+ E AllBits>
+struct BitFlagsEnumValidator
+{
+ static bool IsLegalValue(E e)
+ {
+ return (e & AllBits) == e;
+ }
+};
+
+/**
+ * Specialization of EnumSerializer for enums with contiguous enum values.
+ *
+ * Provide two values: MinLegal, HighBound. An enum value x will be
+ * considered legal if MinLegal <= x < HighBound.
+ *
+ * For example, following is definition of serializer for enum type FOO.
+ * \code
+ * enum FOO { FOO_FIRST, FOO_SECOND, FOO_LAST, NUM_FOO };
+ *
+ * template <>
+ * struct ParamTraits<FOO>:
+ * public ContiguousEnumSerializer<FOO, FOO_FIRST, NUM_FOO> {};
+ * \endcode
+ * FOO_FIRST, FOO_SECOND, and FOO_LAST are valid value.
+ */
+template <typename E,
+ E MinLegal,
+ E HighBound>
+struct ContiguousEnumSerializer
+ : EnumSerializer<E,
+ ContiguousEnumValidator<E, MinLegal, HighBound>>
+{};
+
+/**
+ * Specialization of EnumSerializer for enums representing bit flags.
+ *
+ * Provide one value: AllBits. An enum value x will be
+ * considered legal if (x & AllBits) == x;
+ *
+ * Example:
+ * \code
+ * enum FOO {
+ * FOO_FIRST = 1 << 0,
+ * FOO_SECOND = 1 << 1,
+ * FOO_LAST = 1 << 2,
+ * ALL_BITS = (1 << 3) - 1
+ * };
+ *
+ * template <>
+ * struct ParamTraits<FOO>:
+ * public BitFlagsEnumSerializer<FOO, FOO::ALL_BITS> {};
+ * \endcode
+ */
+template <typename E,
+ E AllBits>
+struct BitFlagsEnumSerializer
+ : EnumSerializer<E,
+ BitFlagsEnumValidator<E, AllBits>>
+{};
+
+template <>
+struct ParamTraits<base::ChildPrivileges>
+ : public ContiguousEnumSerializer<base::ChildPrivileges,
+ base::PRIVILEGES_DEFAULT,
+ base::PRIVILEGES_LAST>
+{ };
+
+template<>
+struct ParamTraits<int8_t>
+{
+ typedef int8_t paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteBytes(&aParam, sizeof(aParam));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
+ }
+};
+
+template<>
+struct ParamTraits<uint8_t>
+{
+ typedef uint8_t paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteBytes(&aParam, sizeof(aParam));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
+ }
+};
+
+#if !defined(OS_POSIX)
+// See above re: keeping definitions in sync
+template<>
+struct ParamTraits<base::FileDescriptor>
+{
+ typedef base::FileDescriptor paramType;
+ static void Write(Message* aMsg, const paramType& aParam) {
+ NS_RUNTIMEABORT("FileDescriptor isn't meaningful on this platform");
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+ NS_RUNTIMEABORT("FileDescriptor isn't meaningful on this platform");
+ return false;
+ }
+};
+#endif // !defined(OS_POSIX)
+
+template <>
+struct ParamTraits<nsACString>
+{
+ typedef nsACString paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ bool isVoid = aParam.IsVoid();
+ aMsg->WriteBool(isVoid);
+
+ if (isVoid)
+ // represents a nullptr pointer
+ return;
+
+ uint32_t length = aParam.Length();
+ WriteParam(aMsg, length);
+ aMsg->WriteBytes(aParam.BeginReading(), length);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ bool isVoid;
+ if (!aMsg->ReadBool(aIter, &isVoid))
+ return false;
+
+ if (isVoid) {
+ aResult->SetIsVoid(true);
+ return true;
+ }
+
+ uint32_t length;
+ if (!ReadParam(aMsg, aIter, &length)) {
+ return false;
+ }
+ aResult->SetLength(length);
+
+ return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), length);
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ if (aParam.IsVoid())
+ aLog->append(L"(NULL)");
+ else
+ aLog->append(UTF8ToWide(aParam.BeginReading()));
+ }
+};
+
+template <>
+struct ParamTraits<nsAString>
+{
+ typedef nsAString paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ bool isVoid = aParam.IsVoid();
+ aMsg->WriteBool(isVoid);
+
+ if (isVoid)
+ // represents a nullptr pointer
+ return;
+
+ uint32_t length = aParam.Length();
+ WriteParam(aMsg, length);
+ aMsg->WriteBytes(aParam.BeginReading(), length * sizeof(char16_t));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ bool isVoid;
+ if (!aMsg->ReadBool(aIter, &isVoid))
+ return false;
+
+ if (isVoid) {
+ aResult->SetIsVoid(true);
+ return true;
+ }
+
+ uint32_t length;
+ if (!ReadParam(aMsg, aIter, &length)) {
+ return false;
+ }
+ aResult->SetLength(length);
+
+ return aMsg->ReadBytesInto(aIter, aResult->BeginWriting(), length * sizeof(char16_t));
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ if (aParam.IsVoid())
+ aLog->append(L"(NULL)");
+ else {
+#ifdef WCHAR_T_IS_UTF16
+ aLog->append(reinterpret_cast<const wchar_t*>(aParam.BeginReading()));
+#else
+ uint32_t length = aParam.Length();
+ for (uint32_t index = 0; index < length; index++) {
+ aLog->push_back(std::wstring::value_type(aParam[index]));
+ }
+#endif
+ }
+ }
+};
+
+template <>
+struct ParamTraits<nsCString> : ParamTraits<nsACString>
+{
+ typedef nsCString paramType;
+};
+
+template <>
+struct ParamTraits<nsLiteralCString> : ParamTraits<nsACString>
+{
+ typedef nsLiteralCString paramType;
+};
+
+#ifdef MOZILLA_INTERNAL_API
+
+template<>
+struct ParamTraits<nsAutoCString> : ParamTraits<nsCString>
+{
+ typedef nsAutoCString paramType;
+};
+
+#endif // MOZILLA_INTERNAL_API
+
+template <>
+struct ParamTraits<nsString> : ParamTraits<nsAString>
+{
+ typedef nsString paramType;
+};
+
+template <>
+struct ParamTraits<nsLiteralString> : ParamTraits<nsAString>
+{
+ typedef nsLiteralString paramType;
+};
+
+// Pickle::ReadBytes and ::WriteBytes take the length in ints, so we must
+// ensure there is no overflow. This returns |false| if it would overflow.
+// Otherwise, it returns |true| and places the byte length in |aByteLength|.
+bool ByteLengthIsValid(uint32_t aNumElements, size_t aElementSize, int* aByteLength);
+
+// Note: IPDL will sometimes codegen specialized implementations of
+// nsTArray serialization and deserialization code in
+// implementSpecialArrayPickling(). This is needed when ParamTraits<E>
+// is not defined.
+template <typename E>
+struct ParamTraits<nsTArray<E>>
+{
+ typedef nsTArray<E> paramType;
+
+ // We write arrays of integer or floating-point data using a single pickling
+ // call, rather than writing each element individually. We deliberately do
+ // not use mozilla::IsPod here because it is perfectly reasonable to have
+ // a data structure T for which IsPod<T>::value is true, yet also have a
+ // ParamTraits<T> specialization.
+ static const bool sUseWriteBytes = (mozilla::IsIntegral<E>::value ||
+ mozilla::IsFloatingPoint<E>::value);
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ uint32_t length = aParam.Length();
+ WriteParam(aMsg, length);
+
+ if (sUseWriteBytes) {
+ int pickledLength = 0;
+ MOZ_RELEASE_ASSERT(ByteLengthIsValid(length, sizeof(E), &pickledLength));
+ aMsg->WriteBytes(aParam.Elements(), pickledLength);
+ } else {
+ const E* elems = aParam.Elements();
+ for (uint32_t index = 0; index < length; index++) {
+ WriteParam(aMsg, elems[index]);
+ }
+ }
+ }
+
+ // This method uses infallible allocation so that an OOM failure will
+ // show up as an OOM crash rather than an IPC FatalError.
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ uint32_t length;
+ if (!ReadParam(aMsg, aIter, &length)) {
+ return false;
+ }
+
+ if (sUseWriteBytes) {
+ int pickledLength = 0;
+ if (!ByteLengthIsValid(length, sizeof(E), &pickledLength)) {
+ return false;
+ }
+
+ E* elements = aResult->AppendElements(length);
+ return aMsg->ReadBytesInto(aIter, elements, pickledLength);
+ } else {
+ aResult->SetCapacity(length);
+
+ for (uint32_t index = 0; index < length; index++) {
+ E* element = aResult->AppendElement();
+ if (!ReadParam(aMsg, aIter, element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ for (uint32_t index = 0; index < aParam.Length(); index++) {
+ if (index) {
+ aLog->append(L" ");
+ }
+ LogParam(aParam[index], aLog);
+ }
+ }
+};
+
+template<typename E>
+struct ParamTraits<FallibleTArray<E>>
+{
+ typedef FallibleTArray<E> paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, static_cast<const nsTArray<E>&>(aParam));
+ }
+
+ // Deserialize the array infallibly, but return a FallibleTArray.
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ nsTArray<E> temp;
+ if (!ReadParam(aMsg, aIter, &temp))
+ return false;
+
+ aResult->SwapElements(temp);
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ LogParam(static_cast<const nsTArray<E>&>(aParam), aLog);
+ }
+};
+
+template<typename E, size_t N>
+struct ParamTraits<AutoTArray<E, N>> : ParamTraits<nsTArray<E>>
+{
+ typedef AutoTArray<E, N> paramType;
+};
+
+template<>
+struct ParamTraits<float>
+{
+ typedef float paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aMsg->WriteBytes(&aParam, sizeof(paramType));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return aMsg->ReadBytesInto(aIter, aResult, sizeof(*aResult));
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"%g", aParam));
+ }
+};
+
+template <>
+struct ParamTraits<nsCSSPropertyID>
+ : public ContiguousEnumSerializer<nsCSSPropertyID,
+ eCSSProperty_UNKNOWN,
+ eCSSProperty_COUNT>
+{};
+
+template<>
+struct ParamTraits<mozilla::void_t>
+{
+ typedef mozilla::void_t paramType;
+ static void Write(Message* aMsg, const paramType& aParam) { }
+ static bool
+ Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ *aResult = paramType();
+ return true;
+ }
+};
+
+template<>
+struct ParamTraits<mozilla::null_t>
+{
+ typedef mozilla::null_t paramType;
+ static void Write(Message* aMsg, const paramType& aParam) { }
+ static bool
+ Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ *aResult = paramType();
+ return true;
+ }
+};
+
+template<>
+struct ParamTraits<nsID>
+{
+ typedef nsID paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.m0);
+ WriteParam(aMsg, aParam.m1);
+ WriteParam(aMsg, aParam.m2);
+ for (unsigned int i = 0; i < mozilla::ArrayLength(aParam.m3); i++) {
+ WriteParam(aMsg, aParam.m3[i]);
+ }
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ if(!ReadParam(aMsg, aIter, &(aResult->m0)) ||
+ !ReadParam(aMsg, aIter, &(aResult->m1)) ||
+ !ReadParam(aMsg, aIter, &(aResult->m2)))
+ return false;
+
+ for (unsigned int i = 0; i < mozilla::ArrayLength(aResult->m3); i++)
+ if (!ReadParam(aMsg, aIter, &(aResult->m3[i])))
+ return false;
+
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(L"{");
+ aLog->append(StringPrintf(L"%8.8X-%4.4X-%4.4X-",
+ aParam.m0,
+ aParam.m1,
+ aParam.m2));
+ for (unsigned int i = 0; i < mozilla::ArrayLength(aParam.m3); i++)
+ aLog->append(StringPrintf(L"%2.2X", aParam.m3[i]));
+ aLog->append(L"}");
+ }
+};
+
+template<>
+struct ParamTraits<mozilla::TimeDuration>
+{
+ typedef mozilla::TimeDuration paramType;
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mValue);
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ReadParam(aMsg, aIter, &aResult->mValue);
+ };
+};
+
+template<>
+struct ParamTraits<mozilla::TimeStamp>
+{
+ typedef mozilla::TimeStamp paramType;
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mValue);
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ReadParam(aMsg, aIter, &aResult->mValue);
+ };
+};
+
+#ifdef XP_WIN
+
+template<>
+struct ParamTraits<mozilla::TimeStampValue>
+{
+ typedef mozilla::TimeStampValue paramType;
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mGTC);
+ WriteParam(aMsg, aParam.mQPC);
+ WriteParam(aMsg, aParam.mHasQPC);
+ WriteParam(aMsg, aParam.mIsNull);
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return (ReadParam(aMsg, aIter, &aResult->mGTC) &&
+ ReadParam(aMsg, aIter, &aResult->mQPC) &&
+ ReadParam(aMsg, aIter, &aResult->mHasQPC) &&
+ ReadParam(aMsg, aIter, &aResult->mIsNull));
+ }
+};
+
+#endif
+
+template <>
+struct ParamTraits<mozilla::dom::ipc::StructuredCloneData>
+{
+ typedef mozilla::dom::ipc::StructuredCloneData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aParam.WriteIPCParams(aMsg);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return aResult->ReadIPCParams(aMsg, aIter);
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ LogParam(aParam.DataLength(), aLog);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::net::WebSocketFrameData>
+{
+ typedef mozilla::net::WebSocketFrameData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ aParam.WriteIPCParams(aMsg);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return aResult->ReadIPCParams(aMsg, aIter);
+ }
+};
+
+template <>
+struct ParamTraits<JSStructuredCloneData>
+{
+ typedef JSStructuredCloneData paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t)));
+ WriteParam(aMsg, aParam.Size());
+ auto iter = aParam.Iter();
+ while (!iter.Done()) {
+ aMsg->WriteBytes(iter.Data(), iter.RemainingInSegment(), sizeof(uint64_t));
+ iter.Advance(aParam, iter.RemainingInSegment());
+ }
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ size_t length = 0;
+ if (!ReadParam(aMsg, aIter, &length)) {
+ return false;
+ }
+ MOZ_ASSERT(!(length % sizeof(uint64_t)));
+
+ mozilla::BufferList<InfallibleAllocPolicy> buffers(0, 0, 4096);
+
+ // Borrowing is not suitable to use for IPC to hand out data
+ // because we often want to store the data somewhere for
+ // processing after IPC has released the underlying buffers. One
+ // case is PContentChild::SendGetXPCOMProcessAttributes. We can't
+ // return a borrowed buffer because the out param outlives the
+ // IPDL callback.
+ if (length && !aMsg->ExtractBuffers(aIter, length, &buffers, sizeof(uint64_t))) {
+ return false;
+ }
+
+ bool success;
+ mozilla::BufferList<js::SystemAllocPolicy> out =
+ buffers.MoveFallible<js::SystemAllocPolicy>(&success);
+ if (!success) {
+ return false;
+ }
+
+ *aResult = JSStructuredCloneData(Move(out));
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::SerializedStructuredCloneBuffer>
+{
+ typedef mozilla::SerializedStructuredCloneBuffer paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.data);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ReadParam(aMsg, aIter, &aResult->data);
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ LogParam(aParam.data.Size(), aLog);
+ }
+};
+
+template <>
+struct ParamTraits<nsIWidget::TouchPointerState>
+ : public BitFlagsEnumSerializer<nsIWidget::TouchPointerState,
+ nsIWidget::TouchPointerState::ALL_BITS>
+{
+};
+
+template<class T>
+struct ParamTraits<mozilla::Maybe<T>>
+{
+ typedef mozilla::Maybe<T> paramType;
+
+ static void Write(Message* msg, const paramType& param)
+ {
+ if (param.isSome()) {
+ WriteParam(msg, true);
+ WriteParam(msg, param.value());
+ } else {
+ WriteParam(msg, false);
+ }
+ }
+
+ static bool Read(const Message* msg, PickleIterator* iter, paramType* result)
+ {
+ bool isSome;
+ if (!ReadParam(msg, iter, &isSome)) {
+ return false;
+ }
+ if (isSome) {
+ T tmp;
+ if (!ReadParam(msg, iter, &tmp)) {
+ return false;
+ }
+ *result = mozilla::Some(mozilla::Move(tmp));
+ } else {
+ *result = mozilla::Nothing();
+ }
+ return true;
+ }
+};
+
+} /* namespace IPC */
+
+#endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */
diff --git a/ipc/glue/IPCStream.ipdlh b/ipc/glue/IPCStream.ipdlh
new file mode 100644
index 000000000..3033ad7ff
--- /dev/null
+++ b/ipc/glue/IPCStream.ipdlh
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 PSendStream;
+include BlobTypes;
+include InputStreamParams;
+
+namespace mozilla {
+namespace ipc {
+
+// Do not use this directly. See IPCStream below.
+struct InputStreamParamsWithFds
+{
+ InputStreamParams stream;
+ OptionalFileDescriptorSet optionalFds;
+};
+
+// Use IPCStream or OptionalIPCStream in your ipdl to represent serialized
+// nsIInputStreams. Then use AutoIPCStream from IPCStreamUtils.h to perform
+// the serialization.
+union IPCStream
+{
+ InputStreamParamsWithFds;
+ PSendStream;
+};
+
+union OptionalIPCStream
+{
+ IPCStream;
+ void_t;
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/IPCStreamUtils.cpp b/ipc/glue/IPCStreamUtils.cpp
new file mode 100644
index 000000000..3bb351184
--- /dev/null
+++ b/ipc/glue/IPCStreamUtils.cpp
@@ -0,0 +1,495 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "IPCStreamUtils.h"
+
+#include "nsIIPCSerializableInputStream.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/nsIContentChild.h"
+#include "mozilla/dom/PContentParent.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/ipc/FileDescriptorSetChild.h"
+#include "mozilla/ipc/FileDescriptorSetParent.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/ipc/SendStream.h"
+#include "mozilla/Unused.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIPipe.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+namespace ipc {
+
+namespace {
+
+// These serialization and cleanup functions could be externally exposed. For
+// now, though, keep them private to encourage use of the safer RAII
+// AutoIPCStream class.
+
+template<typename M>
+void
+SerializeInputStreamWithFdsChild(nsIInputStream* aStream,
+ IPCStream& aValue,
+ M* aManager)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aManager);
+
+ // First attempt simple stream serialization
+ nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+ do_QueryInterface(aStream);
+ if (!serializable) {
+ MOZ_CRASH("Input stream is not serializable!");
+ }
+
+ aValue = InputStreamParamsWithFds();
+ InputStreamParamsWithFds& streamWithFds =
+ aValue.get_InputStreamParamsWithFds();
+
+ AutoTArray<FileDescriptor, 4> fds;
+ serializable->Serialize(streamWithFds.stream(), fds);
+
+ if (streamWithFds.stream().type() == InputStreamParams::T__None) {
+ MOZ_CRASH("Serialize failed!");
+ }
+
+ if (fds.IsEmpty()) {
+ streamWithFds.optionalFds() = void_t();
+ } else {
+ PFileDescriptorSetChild* fdSet =
+ aManager->SendPFileDescriptorSetConstructor(fds[0]);
+ for (uint32_t i = 1; i < fds.Length(); ++i) {
+ Unused << fdSet->SendAddFileDescriptor(fds[i]);
+ }
+
+ streamWithFds.optionalFds() = fdSet;
+ }
+}
+
+template<typename M>
+void
+SerializeInputStreamWithFdsParent(nsIInputStream* aStream,
+ IPCStream& aValue,
+ M* aManager)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aManager);
+
+ // First attempt simple stream serialization
+ nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+ do_QueryInterface(aStream);
+ if (!serializable) {
+ MOZ_CRASH("Input stream is not serializable!");
+ }
+
+ aValue = InputStreamParamsWithFds();
+ InputStreamParamsWithFds& streamWithFds =
+ aValue.get_InputStreamParamsWithFds();
+
+ AutoTArray<FileDescriptor, 4> fds;
+ serializable->Serialize(streamWithFds.stream(), fds);
+
+ if (streamWithFds.stream().type() == InputStreamParams::T__None) {
+ MOZ_CRASH("Serialize failed!");
+ }
+
+ streamWithFds.optionalFds() = void_t();
+ if (!fds.IsEmpty()) {
+ PFileDescriptorSetParent* fdSet =
+ aManager->SendPFileDescriptorSetConstructor(fds[0]);
+ for (uint32_t i = 1; i < fds.Length(); ++i) {
+ if (NS_WARN_IF(!fdSet->SendAddFileDescriptor(fds[i]))) {
+ Unused << PFileDescriptorSetParent::Send__delete__(fdSet);
+ fdSet = nullptr;
+ break;
+ }
+ }
+
+ if (fdSet) {
+ streamWithFds.optionalFds() = fdSet;
+ }
+ }
+}
+
+template<typename M>
+void
+SerializeInputStream(nsIInputStream* aStream, IPCStream& aValue, M* aManager)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aManager);
+
+ // If a stream is known to be larger than 1MB, prefer sending it in chunks.
+ const uint64_t kTooLargeStream = 1024 * 1024;
+
+ // First attempt simple stream serialization
+ nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+ do_QueryInterface(aStream);
+ uint64_t expectedLength =
+ serializable ? serializable->ExpectedSerializedLength().valueOr(0) : 0;
+ if (serializable && expectedLength < kTooLargeStream) {
+ SerializeInputStreamWithFdsChild(aStream, aValue, aManager);
+ return;
+ }
+
+ // As a fallback, attempt to stream the data across using a SendStream
+ // actor. For blocking streams, create a nonblocking pipe instead,
+ nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
+ if (!asyncStream) {
+ const uint32_t kBufferSize = 32768; // matches SendStream buffer size.
+ nsCOMPtr<nsIAsyncOutputStream> sink;
+ DebugOnly<nsresult> rv = NS_NewPipe2(getter_AddRefs(asyncStream),
+ getter_AddRefs(sink),
+ true,
+ false,
+ kBufferSize,
+ UINT32_MAX);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsIEventTarget> target =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+
+ rv = NS_AsyncCopy(aStream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS, kBufferSize);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ MOZ_ASSERT(asyncStream);
+ aValue = SendStreamChild::Create(asyncStream, aManager);
+
+ if (!aValue.get_PSendStreamChild()) {
+ MOZ_CRASH("SendStream creation failed!");
+ }
+}
+
+template<typename M>
+void
+SerializeInputStream(nsIInputStream* aStream, OptionalIPCStream& aValue,
+ M* aManager)
+{
+ if (!aStream) {
+ aValue = void_t();
+ return;
+ }
+
+ aValue = IPCStream();
+ SerializeInputStream(aStream, aValue.get_IPCStream(),
+ aManager);
+}
+
+void
+CleanupIPCStream(IPCStream& aValue, bool aConsumedByIPC)
+{
+ if (aValue.type() == IPCStream::T__None) {
+ return;
+ }
+
+ if (aValue.type() == IPCStream::TInputStreamParamsWithFds) {
+
+ InputStreamParamsWithFds& streamWithFds =
+ aValue.get_InputStreamParamsWithFds();
+
+ // Cleanup file descriptors if necessary
+ if (streamWithFds.optionalFds().type() ==
+ OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
+
+ AutoTArray<FileDescriptor, 4> fds;
+
+ auto fdSetActor = static_cast<FileDescriptorSetChild*>(
+ streamWithFds.optionalFds().get_PFileDescriptorSetChild());
+ MOZ_ASSERT(fdSetActor);
+
+ // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
+ // unconditionally forget them here. The fds themselves are auto-closed in
+ // ~FileDescriptor since they originated in this process.
+ fdSetActor->ForgetFileDescriptors(fds);
+
+ if (!aConsumedByIPC) {
+ Unused << fdSetActor->Send__delete__(fdSetActor);
+ }
+
+ } else if (streamWithFds.optionalFds().type() ==
+ OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
+
+ AutoTArray<FileDescriptor, 4> fds;
+
+ auto fdSetActor = static_cast<FileDescriptorSetParent*>(
+ streamWithFds.optionalFds().get_PFileDescriptorSetParent());
+ MOZ_ASSERT(fdSetActor);
+
+ // FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
+ // unconditionally forget them here. The fds themselves are auto-closed in
+ // ~FileDescriptor since they originated in this process.
+ fdSetActor->ForgetFileDescriptors(fds);
+
+ if (!aConsumedByIPC) {
+ Unused << fdSetActor->Send__delete__(fdSetActor);
+ }
+ }
+
+ return;
+ }
+
+ MOZ_ASSERT(aValue.type() == IPCStream::TPSendStreamChild);
+
+ auto sendStream =
+ static_cast<SendStreamChild*>(aValue.get_PSendStreamChild());
+
+ if (!aConsumedByIPC) {
+ sendStream->StartDestroy();
+ return;
+ }
+
+ // If the SendStream was taken to be sent to the parent, then we need to
+ // start it before forgetting about it.
+ sendStream->Start();
+}
+
+void
+CleanupIPCStream(OptionalIPCStream& aValue, bool aConsumedByIPC)
+{
+ if (aValue.type() == OptionalIPCStream::Tvoid_t) {
+ return;
+ }
+
+ CleanupIPCStream(aValue.get_IPCStream(), aConsumedByIPC);
+}
+
+} // anonymous namespace
+
+already_AddRefed<nsIInputStream>
+DeserializeIPCStream(const IPCStream& aValue)
+{
+ if (aValue.type() == IPCStream::TPSendStreamParent) {
+ auto sendStream =
+ static_cast<SendStreamParent*>(aValue.get_PSendStreamParent());
+ return sendStream->TakeReader();
+ }
+
+ // Note, we explicitly do not support deserializing the PSendStream actor on
+ // the child side. It can only be sent from child to parent.
+ MOZ_ASSERT(aValue.type() == IPCStream::TInputStreamParamsWithFds);
+
+ const InputStreamParamsWithFds& streamWithFds =
+ aValue.get_InputStreamParamsWithFds();
+
+ AutoTArray<FileDescriptor, 4> fds;
+ if (streamWithFds.optionalFds().type() ==
+ OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
+
+ auto fdSetActor = static_cast<FileDescriptorSetParent*>(
+ streamWithFds.optionalFds().get_PFileDescriptorSetParent());
+ MOZ_ASSERT(fdSetActor);
+
+ fdSetActor->ForgetFileDescriptors(fds);
+ MOZ_ASSERT(!fds.IsEmpty());
+
+ if (!fdSetActor->Send__delete__(fdSetActor)) {
+ // child process is gone, warn and allow actor to clean up normally
+ NS_WARNING("Failed to delete fd set actor.");
+ }
+ } else if (streamWithFds.optionalFds().type() ==
+ OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
+
+ auto fdSetActor = static_cast<FileDescriptorSetChild*>(
+ streamWithFds.optionalFds().get_PFileDescriptorSetChild());
+ MOZ_ASSERT(fdSetActor);
+
+ fdSetActor->ForgetFileDescriptors(fds);
+ MOZ_ASSERT(!fds.IsEmpty());
+
+ Unused << fdSetActor->Send__delete__(fdSetActor);
+ }
+
+ return DeserializeInputStream(streamWithFds.stream(), fds);
+}
+
+already_AddRefed<nsIInputStream>
+DeserializeIPCStream(const OptionalIPCStream& aValue)
+{
+ if (aValue.type() == OptionalIPCStream::Tvoid_t) {
+ return nullptr;
+ }
+
+ return DeserializeIPCStream(aValue.get_IPCStream());
+}
+
+namespace {
+
+void
+AssertValidValueToTake(const IPCStream& aVal)
+{
+ MOZ_ASSERT(aVal.type() == IPCStream::TPSendStreamChild ||
+ aVal.type() == IPCStream::TInputStreamParamsWithFds);
+}
+
+void
+AssertValidValueToTake(const OptionalIPCStream& aVal)
+{
+ MOZ_ASSERT(aVal.type() == OptionalIPCStream::Tvoid_t ||
+ aVal.type() == OptionalIPCStream::TIPCStream);
+ if (aVal.type() == OptionalIPCStream::TIPCStream) {
+ AssertValidValueToTake(aVal.get_IPCStream());
+ }
+}
+
+} // anonymous namespace
+
+AutoIPCStream::AutoIPCStream()
+ : mInlineValue(void_t())
+ , mValue(nullptr)
+ , mOptionalValue(&mInlineValue)
+ , mTaken(false)
+{
+}
+
+AutoIPCStream::AutoIPCStream(IPCStream& aTarget)
+ : mInlineValue(void_t())
+ , mValue(&aTarget)
+ , mOptionalValue(nullptr)
+ , mTaken(false)
+{
+}
+
+AutoIPCStream::AutoIPCStream(OptionalIPCStream& aTarget)
+ : mInlineValue(void_t())
+ , mValue(nullptr)
+ , mOptionalValue(&aTarget)
+ , mTaken(false)
+{
+ *mOptionalValue = void_t();
+}
+
+AutoIPCStream::~AutoIPCStream()
+{
+ MOZ_ASSERT(mValue || mOptionalValue);
+ if (mValue && IsSet()) {
+ CleanupIPCStream(*mValue, mTaken);
+ } else {
+ CleanupIPCStream(*mOptionalValue, mTaken);
+ }
+}
+
+void
+AutoIPCStream::Serialize(nsIInputStream* aStream, dom::nsIContentChild* aManager)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(mValue || mOptionalValue);
+ MOZ_ASSERT(!mTaken);
+ MOZ_ASSERT(!IsSet());
+
+ if (mValue) {
+ SerializeInputStream(aStream, *mValue, aManager);
+ AssertValidValueToTake(*mValue);
+ } else {
+ SerializeInputStream(aStream, *mOptionalValue, aManager);
+ AssertValidValueToTake(*mOptionalValue);
+ }
+}
+
+void
+AutoIPCStream::Serialize(nsIInputStream* aStream, PBackgroundChild* aManager)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(mValue || mOptionalValue);
+ MOZ_ASSERT(!mTaken);
+ MOZ_ASSERT(!IsSet());
+
+ if (mValue) {
+ SerializeInputStream(aStream, *mValue, aManager);
+ AssertValidValueToTake(*mValue);
+ } else {
+ SerializeInputStream(aStream, *mOptionalValue, aManager);
+ AssertValidValueToTake(*mOptionalValue);
+ }
+}
+
+void
+AutoIPCStream::Serialize(nsIInputStream* aStream, dom::PContentParent* aManager)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(mValue || mOptionalValue);
+ MOZ_ASSERT(!mTaken);
+ MOZ_ASSERT(!IsSet());
+
+ if (mValue) {
+ SerializeInputStreamWithFdsParent(aStream, *mValue, aManager);
+ AssertValidValueToTake(*mValue);
+ } else {
+ SerializeInputStreamWithFdsParent(aStream, *mOptionalValue, aManager);
+ AssertValidValueToTake(*mOptionalValue);
+ }
+}
+
+void
+AutoIPCStream::Serialize(nsIInputStream* aStream, PBackgroundParent* aManager)
+{
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(mValue || mOptionalValue);
+ MOZ_ASSERT(!mTaken);
+ MOZ_ASSERT(!IsSet());
+
+ if (mValue) {
+ SerializeInputStreamWithFdsParent(aStream, *mValue, aManager);
+ AssertValidValueToTake(*mValue);
+ } else {
+ SerializeInputStreamWithFdsParent(aStream, *mOptionalValue, aManager);
+ AssertValidValueToTake(*mOptionalValue);
+ }
+}
+
+bool
+AutoIPCStream::IsSet() const
+{
+ MOZ_ASSERT(mValue || mOptionalValue);
+ if (mValue) {
+ return mValue->type() != IPCStream::T__None;
+ } else {
+ return mOptionalValue->type() != OptionalIPCStream::Tvoid_t &&
+ mOptionalValue->get_IPCStream().type() != IPCStream::T__None;
+ }
+}
+
+IPCStream&
+AutoIPCStream::TakeValue()
+{
+ MOZ_ASSERT(mValue || mOptionalValue);
+ MOZ_ASSERT(!mTaken);
+ MOZ_ASSERT(IsSet());
+
+ mTaken = true;
+
+ if (mValue) {
+ AssertValidValueToTake(*mValue);
+ return *mValue;
+ }
+
+ IPCStream& value =
+ mOptionalValue->get_IPCStream();
+
+ AssertValidValueToTake(value);
+ return value;
+}
+
+OptionalIPCStream&
+AutoIPCStream::TakeOptionalValue()
+{
+ MOZ_ASSERT(!mTaken);
+ MOZ_ASSERT(!mValue);
+ MOZ_ASSERT(mOptionalValue);
+ mTaken = true;
+ AssertValidValueToTake(*mOptionalValue);
+ return *mOptionalValue;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/IPCStreamUtils.h b/ipc/glue/IPCStreamUtils.h
new file mode 100644
index 000000000..a20f8a651
--- /dev/null
+++ b/ipc/glue/IPCStreamUtils.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_IPCStreamUtils_h
+#define mozilla_ipc_IPCStreamUtils_h
+
+#include "mozilla/ipc/IPCStream.h"
+#include "nsIInputStream.h"
+
+namespace mozilla {
+
+namespace dom {
+class nsIContentChild;
+class PContentParent;
+}
+
+namespace ipc {
+
+class PBackgroundChild;
+class PBackgroundParent;
+
+// Deserialize an IPCStream received from an actor call. These methods
+// work in both the child and parent.
+already_AddRefed<nsIInputStream>
+DeserializeIPCStream(const IPCStream& aValue);
+
+already_AddRefed<nsIInputStream>
+DeserializeIPCStream(const OptionalIPCStream& aValue);
+
+// RAII helper class that serializes an nsIInputStream into an IPCStream struct.
+// Any file descriptor or PSendStream actors are automatically managed
+// correctly.
+//
+// Here is a simple example:
+//
+// // in ipdl file
+// Protocol PMyStuff
+// {
+// parent:
+// async DoStuff(IPCStream aStream);
+// child:
+// async StuffDone(IPCStream aStream);
+// };
+//
+// // in child c++ code
+// void CallDoStuff(PMyStuffChild* aActor, nsIInputStream* aStream)
+// {
+// AutoIPCStream autoStream;
+// autoStream.Serialize(aStream, aActor->Manager());
+// aActor->SendDoStuff(autoStream.TakeValue());
+// }
+//
+// // in parent c++ code
+// bool
+// MyStuffParent::RecvDoStuff(const IPCStream& aIPCStream) {
+// nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aIPCStream);
+// // Do something with stream...
+//
+// // You can also serialize streams from parent-to-child as long as
+// // they don't require PSendStream actor support.
+// AutoIPCStream anotherStream;
+// anotherStream.Serialize(mFileStream, Manager());
+// SendStuffDone(anotherStream.TakeValue());
+// }
+//
+// The AutoIPCStream RAII class may also be used if your stream is embedded
+// in a more complex IPDL structure. In this case you attach the AutoIPCStream
+// to the embedded IPCStream and call TakeValue() after you pass the structure.
+// For example:
+//
+// // in ipdl file
+// struct Stuff
+// {
+// IPCStream stream;
+// nsCString name;
+// };
+//
+// Protocol PMyStuff
+// {
+// parent:
+// async DoStuff(Stuff aStream);
+// };
+//
+// // in child c++ code
+// void CallDoStuff(PMyStuffChild* aActor, nsIInputStream* aStream)
+// {
+// Stuff stuff;
+// AutoIPCStream autoStream(stuff.stream()); // attach to IPCStream here
+// autoStream.Serialize(aStream, aActor->Manager());
+// aActor->SendDoStuff(stuff);
+// autoStream.TakeValue(); // call take value after send
+// }
+//
+// // in parent c++ code
+// bool
+// MyStuffParent::RecvDoStuff(const Stuff& aStuff) {
+// nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aStuff.stream());
+// /* do something with the nsIInputStream */
+// }
+//
+// The AutoIPCStream class also supports OptionalIPCStream values. As long as
+// you did not initialize the object with a non-optional IPCStream, you can call
+// TakeOptionalValue() instead.
+//
+// The AutoIPCStream class can also be used to serialize nsIInputStream objects
+// on the parent side to send to the child. Currently, however, this only
+// works for directly serializable stream types. The PSendStream actor mechanism
+// is not supported in this direction yet.
+//
+// Like SerializeInputStream(), the AutoIPCStream will crash if
+// serialization cannot be completed.
+//
+// NOTE: This is not a MOZ_STACK_CLASS so that it can be more easily integrated
+// with complex ipdl structures. For example, you may want to create an
+// array of RAII AutoIPCStream objects or build your own wrapping
+// RAII object to handle other actors that need to be cleaned up.
+class AutoIPCStream final
+{
+ OptionalIPCStream mInlineValue;
+ IPCStream* mValue;
+ OptionalIPCStream* mOptionalValue;
+ bool mTaken;
+
+ bool
+ IsSet() const;
+
+public:
+ // Implicitly create an OptionalIPCStream value. Either
+ // TakeValue() or TakeOptionalValue() can be used.
+ AutoIPCStream();
+
+ // Wrap an existing IPCStream. Only TakeValue() may be
+ // used. If a nullptr nsIInputStream is passed to SerializeOrSend() then
+ // a crash will be forced.
+ explicit AutoIPCStream(IPCStream& aTarget);
+
+ // Wrap an existing OptionalIPCStream. Either TakeValue()
+ // or TakeOptionalValue can be used.
+ explicit AutoIPCStream(OptionalIPCStream& aTarget);
+
+ ~AutoIPCStream();
+
+ // Serialize the input stream or create a SendStream actor using the PContent
+ // manager. If neither of these succeed, then crash. This should only be
+ // used on the main thread.
+ void
+ Serialize(nsIInputStream* aStream, dom::nsIContentChild* aManager);
+
+ // Serialize the input stream or create a SendStream actor using the
+ // PBackground manager. If neither of these succeed, then crash. This can
+ // be called on the main thread or Worker threads.
+ void
+ Serialize(nsIInputStream* aStream, PBackgroundChild* aManager);
+
+ // Serialize the input stream. A PSendStream cannot be used when going
+ // from parent-to-child.
+ void
+ Serialize(nsIInputStream* aStream, dom::PContentParent* aManager);
+
+ // Serialize the input stream. A PSendStream cannot be used when going
+ // from parent-to-child.
+ void
+ Serialize(nsIInputStream* aStream, PBackgroundParent* aManager);
+
+ // Get the IPCStream as a non-optional value. This will
+ // assert if a stream has not been serialized or if it has already been taken.
+ // This should only be called if the value is being, or has already been, sent
+ // to the parent
+ IPCStream&
+ TakeValue();
+
+ // Get the OptionalIPCStream value. This will assert if
+ // the value has already been taken. This should only be called if the value
+ // is being, or has already been, sent to the parent
+ OptionalIPCStream&
+ TakeOptionalValue();
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_IPCStreamUtils_h
diff --git a/ipc/glue/InputStreamParams.ipdlh b/ipc/glue/InputStreamParams.ipdlh
new file mode 100644
index 000000000..eb6869c17
--- /dev/null
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -0,0 +1,96 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 ProtocolTypes;
+
+using struct mozilla::void_t
+ from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace ipc {
+
+struct StringInputStreamParams
+{
+ nsCString data;
+};
+
+struct FileInputStreamParams
+{
+ uint32_t fileDescriptorIndex;
+ int32_t behaviorFlags;
+ int32_t ioFlags;
+};
+
+struct PartialFileInputStreamParams
+{
+ FileInputStreamParams fileStreamParams;
+ uint64_t begin;
+ uint64_t length;
+};
+
+struct TemporaryFileInputStreamParams
+{
+ uint32_t fileDescriptorIndex;
+ uint64_t startPos;
+ uint64_t endPos;
+};
+
+struct MultiplexInputStreamParams
+{
+ InputStreamParams[] streams;
+ uint32_t currentStream;
+ nsresult status;
+ bool startedReadingCurrent;
+};
+
+struct RemoteInputStreamParams
+{
+ nsID id;
+};
+
+// XXX This may only be used for same-process inter-thread communication! The
+// value should be reinterpret_cast'd to nsIInputStream. It carries a
+// reference.
+struct SameProcessInputStreamParams
+{
+ intptr_t addRefedInputStream;
+};
+
+union InputStreamParams
+{
+ StringInputStreamParams;
+ FileInputStreamParams;
+ PartialFileInputStreamParams;
+ TemporaryFileInputStreamParams;
+ BufferedInputStreamParams;
+ MIMEInputStreamParams;
+ MultiplexInputStreamParams;
+ RemoteInputStreamParams;
+ SameProcessInputStreamParams;
+};
+
+union OptionalInputStreamParams
+{
+ void_t;
+ InputStreamParams;
+};
+
+struct BufferedInputStreamParams
+{
+ OptionalInputStreamParams optionalStream;
+ uint32_t bufferSize;
+};
+
+struct MIMEInputStreamParams
+{
+ OptionalInputStreamParams optionalStream;
+ nsCString headers;
+ nsCString contentLength;
+ bool startedReading;
+ bool addContentLength;
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/InputStreamUtils.cpp b/ipc/glue/InputStreamUtils.cpp
new file mode 100644
index 000000000..bbc863efd
--- /dev/null
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "InputStreamUtils.h"
+
+#include "nsIIPCSerializableInputStream.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDebug.h"
+#include "nsID.h"
+#include "nsIXULRuntime.h"
+#include "nsMIMEInputStream.h"
+#include "nsMultiplexInputStream.h"
+#include "nsNetCID.h"
+#include "nsStringStream.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla::dom;
+
+namespace {
+
+NS_DEFINE_CID(kStringInputStreamCID, NS_STRINGINPUTSTREAM_CID);
+NS_DEFINE_CID(kFileInputStreamCID, NS_LOCALFILEINPUTSTREAM_CID);
+NS_DEFINE_CID(kPartialFileInputStreamCID, NS_PARTIALLOCALFILEINPUTSTREAM_CID);
+NS_DEFINE_CID(kBufferedInputStreamCID, NS_BUFFEREDINPUTSTREAM_CID);
+NS_DEFINE_CID(kMIMEInputStreamCID, NS_MIMEINPUTSTREAM_CID);
+NS_DEFINE_CID(kMultiplexInputStreamCID, NS_MULTIPLEXINPUTSTREAM_CID);
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+
+void
+SerializeInputStream(nsIInputStream* aInputStream,
+ InputStreamParams& aParams,
+ nsTArray<FileDescriptor>& aFileDescriptors)
+{
+ MOZ_ASSERT(aInputStream);
+
+ nsCOMPtr<nsIIPCSerializableInputStream> serializable =
+ do_QueryInterface(aInputStream);
+ if (!serializable) {
+ MOZ_CRASH("Input stream is not serializable!");
+ }
+
+ serializable->Serialize(aParams, aFileDescriptors);
+
+ if (aParams.type() == InputStreamParams::T__None) {
+ MOZ_CRASH("Serialize failed!");
+ }
+}
+
+void
+SerializeInputStream(nsIInputStream* aInputStream,
+ OptionalInputStreamParams& aParams,
+ nsTArray<FileDescriptor>& aFileDescriptors)
+{
+ if (aInputStream) {
+ InputStreamParams params;
+ SerializeInputStream(aInputStream, params, aFileDescriptors);
+ aParams = params;
+ }
+ else {
+ aParams = mozilla::void_t();
+ }
+}
+
+already_AddRefed<nsIInputStream>
+DeserializeInputStream(const InputStreamParams& aParams,
+ const nsTArray<FileDescriptor>& aFileDescriptors)
+{
+ nsCOMPtr<nsIInputStream> stream;
+ nsCOMPtr<nsIIPCSerializableInputStream> serializable;
+
+ switch (aParams.type()) {
+ case InputStreamParams::TStringInputStreamParams:
+ serializable = do_CreateInstance(kStringInputStreamCID);
+ break;
+
+ case InputStreamParams::TFileInputStreamParams:
+ serializable = do_CreateInstance(kFileInputStreamCID);
+ break;
+
+ case InputStreamParams::TPartialFileInputStreamParams:
+ serializable = do_CreateInstance(kPartialFileInputStreamCID);
+ break;
+
+ case InputStreamParams::TTemporaryFileInputStreamParams:
+ serializable = new nsTemporaryFileInputStream();
+ break;
+
+ case InputStreamParams::TBufferedInputStreamParams:
+ serializable = do_CreateInstance(kBufferedInputStreamCID);
+ break;
+
+ case InputStreamParams::TMIMEInputStreamParams:
+ serializable = do_CreateInstance(kMIMEInputStreamCID);
+ break;
+
+ case InputStreamParams::TMultiplexInputStreamParams:
+ serializable = do_CreateInstance(kMultiplexInputStreamCID);
+ break;
+
+ // When the input stream already exists in this process, all we need to do
+ // is retrieve the original instead of sending any data over the wire.
+ case InputStreamParams::TRemoteInputStreamParams: {
+ if (NS_WARN_IF(!XRE_IsParentProcess())) {
+ return nullptr;
+ }
+
+ const nsID& id = aParams.get_RemoteInputStreamParams().id();
+
+ RefPtr<BlobImpl> blobImpl = BlobParent::GetBlobImplForID(id);
+
+ MOZ_ASSERT(blobImpl, "Invalid blob contents");
+
+ // If fetching the internal stream fails, we ignore it and return a
+ // null stream.
+ ErrorResult rv;
+ nsCOMPtr<nsIInputStream> stream;
+ blobImpl->GetInternalStream(getter_AddRefs(stream), rv);
+ if (NS_WARN_IF(rv.Failed()) || !stream) {
+ NS_WARNING("Couldn't obtain a valid stream from the blob");
+ rv.SuppressException();
+ }
+ return stream.forget();
+ }
+
+ case InputStreamParams::TSameProcessInputStreamParams: {
+ MOZ_ASSERT(aFileDescriptors.IsEmpty());
+
+ const SameProcessInputStreamParams& params =
+ aParams.get_SameProcessInputStreamParams();
+
+ stream = dont_AddRef(
+ reinterpret_cast<nsIInputStream*>(params.addRefedInputStream()));
+ MOZ_ASSERT(stream);
+
+ return stream.forget();
+ }
+
+ default:
+ MOZ_ASSERT(false, "Unknown params!");
+ return nullptr;
+ }
+
+ MOZ_ASSERT(serializable);
+
+ if (!serializable->Deserialize(aParams, aFileDescriptors)) {
+ MOZ_ASSERT(false, "Deserialize failed!");
+ return nullptr;
+ }
+
+ stream = do_QueryInterface(serializable);
+ MOZ_ASSERT(stream);
+
+ return stream.forget();
+}
+
+already_AddRefed<nsIInputStream>
+DeserializeInputStream(const OptionalInputStreamParams& aParams,
+ const nsTArray<FileDescriptor>& aFileDescriptors)
+{
+ nsCOMPtr<nsIInputStream> stream;
+
+ switch (aParams.type()) {
+ case OptionalInputStreamParams::Tvoid_t:
+ // Leave stream null.
+ break;
+
+ case OptionalInputStreamParams::TInputStreamParams:
+ stream = DeserializeInputStream(aParams.get_InputStreamParams(),
+ aFileDescriptors);
+ break;
+
+ default:
+ MOZ_ASSERT(false, "Unknown params!");
+ }
+
+ return stream.forget();
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/InputStreamUtils.h b/ipc/glue/InputStreamUtils.h
new file mode 100644
index 000000000..215a8cb23
--- /dev/null
+++ b/ipc/glue/InputStreamUtils.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_InputStreamUtils_h
+#define mozilla_ipc_InputStreamUtils_h
+
+#include "mozilla/ipc/InputStreamParams.h"
+#include "nsCOMPtr.h"
+#include "nsIInputStream.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace ipc {
+
+class FileDescriptor;
+
+void
+SerializeInputStream(nsIInputStream* aInputStream,
+ InputStreamParams& aParams,
+ nsTArray<FileDescriptor>& aFileDescriptors);
+
+void
+SerializeInputStream(nsIInputStream* aInputStream,
+ OptionalInputStreamParams& aParams,
+ nsTArray<FileDescriptor>& aFileDescriptors);
+
+already_AddRefed<nsIInputStream>
+DeserializeInputStream(const InputStreamParams& aParams,
+ const nsTArray<FileDescriptor>& aFileDescriptors);
+
+already_AddRefed<nsIInputStream>
+DeserializeInputStream(const OptionalInputStreamParams& aParams,
+ const nsTArray<FileDescriptor>& aFileDescriptors);
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_InputStreamUtils_h
diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp
new file mode 100644
index 000000000..70e2387d5
--- /dev/null
+++ b/ipc/glue/MessageChannel.cpp
@@ -0,0 +1,2560 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Move.h"
+#include "mozilla/SizePrintfMacros.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Logging.h"
+#include "nsAutoPtr.h"
+#include "nsDebug.h"
+#include "nsISupportsImpl.h"
+#include "nsContentUtils.h"
+
+using mozilla::Move;
+
+// Undo the damage done by mozzconf.h
+#undef compress
+
+// Logging seems to be somewhat broken on b2g.
+#ifdef MOZ_B2G
+#define IPC_LOG(...)
+#else
+static mozilla::LazyLogModule sLogModule("ipc");
+#define IPC_LOG(...) MOZ_LOG(sLogModule, LogLevel::Debug, (__VA_ARGS__))
+#endif
+
+/*
+ * IPC design:
+ *
+ * There are three kinds of messages: async, sync, and intr. Sync and intr
+ * messages are blocking.
+ *
+ * Terminology: To dispatch a message Foo is to run the RecvFoo code for
+ * it. This is also called "handling" the message.
+ *
+ * Sync and async messages can sometimes "nest" inside other sync messages
+ * (i.e., while waiting for the sync reply, we can dispatch the inner
+ * message). Intr messages cannot nest. The three possible nesting levels are
+ * NOT_NESTED, NESTED_INSIDE_SYNC, and NESTED_INSIDE_CPOW. The intended uses
+ * are:
+ * NOT_NESTED - most messages.
+ * NESTED_INSIDE_SYNC - CPOW-related messages, which are always sync
+ * and can go in either direction.
+ * NESTED_INSIDE_CPOW - messages where we don't want to dispatch
+ * incoming CPOWs while waiting for the response.
+ * These nesting levels are ordered: NOT_NESTED, NESTED_INSIDE_SYNC,
+ * NESTED_INSIDE_CPOW. Async messages cannot be NESTED_INSIDE_SYNC but they can
+ * be NESTED_INSIDE_CPOW.
+ *
+ * To avoid jank, the parent process is not allowed to send NOT_NESTED sync messages.
+ * When a process is waiting for a response to a sync message
+ * M0, it will dispatch an incoming message M if:
+ * 1. M has a higher nesting level than M0, or
+ * 2. if M has the same nesting level as M0 and we're in the child, or
+ * 3. if M has the same nesting level as M0 and it was sent by the other side
+ * while dispatching M0.
+ * The idea is that messages with higher nesting should take precendence. The
+ * purpose of rule 2 is to handle a race where both processes send to each other
+ * simultaneously. In this case, we resolve the race in favor of the parent (so
+ * the child dispatches first).
+ *
+ * Messages satisfy the following properties:
+ * A. When waiting for a response to a sync message, we won't dispatch any
+ * messages of nesting level.
+ * B. Messages of the same nesting level will be dispatched roughly in the
+ * order they were sent. The exception is when the parent and child send
+ * sync messages to each other simulataneously. In this case, the parent's
+ * message is dispatched first. While it is dispatched, the child may send
+ * further nested messages, and these messages may be dispatched before the
+ * child's original message. We can consider ordering to be preserved here
+ * because we pretend that the child's original message wasn't sent until
+ * after the parent's message is finished being dispatched.
+ *
+ * When waiting for a sync message reply, we dispatch an async message only if
+ * it is NESTED_INSIDE_CPOW. Normally NESTED_INSIDE_CPOW async
+ * messages are sent only from the child. However, the parent can send
+ * NESTED_INSIDE_CPOW async messages when it is creating a bridged protocol.
+ *
+ * Intr messages are blocking and can nest, but they don't participate in the
+ * nesting levels. While waiting for an intr response, all incoming messages are
+ * dispatched until a response is received. When two intr messages race with
+ * each other, a similar scheme is used to ensure that one side wins. The
+ * winning side is chosen based on the message type.
+ *
+ * Intr messages differ from sync messages in that, while sending an intr
+ * message, we may dispatch an async message. This causes some additional
+ * complexity. One issue is that replies can be received out of order. It's also
+ * more difficult to determine whether one message is nested inside
+ * another. Consequently, intr handling uses mOutOfTurnReplies and
+ * mRemoteStackDepthGuess, which are not needed for sync messages.
+ */
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace std;
+
+using mozilla::dom::AutoNoJSAPI;
+using mozilla::dom::ScriptSettingsInitialized;
+using mozilla::MonitorAutoLock;
+using mozilla::MonitorAutoUnlock;
+
+#define IPC_ASSERT(_cond, ...) \
+ do { \
+ if (!(_cond)) \
+ DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__); \
+ } while (0)
+
+static MessageChannel* gParentProcessBlocker;
+
+namespace mozilla {
+namespace ipc {
+
+static const uint32_t kMinTelemetryMessageSize = 8192;
+
+const int32_t MessageChannel::kNoTimeout = INT32_MIN;
+
+// static
+bool MessageChannel::sIsPumpingMessages = false;
+
+enum Direction
+{
+ IN_MESSAGE,
+ OUT_MESSAGE
+};
+
+class MessageChannel::InterruptFrame
+{
+private:
+ enum Semantics
+ {
+ INTR_SEMS,
+ SYNC_SEMS,
+ ASYNC_SEMS
+ };
+
+public:
+ InterruptFrame(Direction direction, const Message* msg)
+ : mMessageName(msg->name()),
+ mMessageRoutingId(msg->routing_id()),
+ mMesageSemantics(msg->is_interrupt() ? INTR_SEMS :
+ msg->is_sync() ? SYNC_SEMS :
+ ASYNC_SEMS),
+ mDirection(direction),
+ mMoved(false)
+ {
+ MOZ_RELEASE_ASSERT(mMessageName);
+ }
+
+ InterruptFrame(InterruptFrame&& aOther)
+ {
+ MOZ_RELEASE_ASSERT(aOther.mMessageName);
+ mMessageName = aOther.mMessageName;
+ aOther.mMessageName = nullptr;
+ mMoved = aOther.mMoved;
+ aOther.mMoved = true;
+
+ mMessageRoutingId = aOther.mMessageRoutingId;
+ mMesageSemantics = aOther.mMesageSemantics;
+ mDirection = aOther.mDirection;
+ }
+
+ ~InterruptFrame()
+ {
+ MOZ_RELEASE_ASSERT(mMessageName || mMoved);
+ }
+
+ InterruptFrame& operator=(InterruptFrame&& aOther)
+ {
+ MOZ_RELEASE_ASSERT(&aOther != this);
+ this->~InterruptFrame();
+ new (this) InterruptFrame(Move(aOther));
+ return *this;
+ }
+
+ bool IsInterruptIncall() const
+ {
+ return INTR_SEMS == mMesageSemantics && IN_MESSAGE == mDirection;
+ }
+
+ bool IsInterruptOutcall() const
+ {
+ return INTR_SEMS == mMesageSemantics && OUT_MESSAGE == mDirection;
+ }
+
+ bool IsOutgoingSync() const {
+ return (mMesageSemantics == INTR_SEMS || mMesageSemantics == SYNC_SEMS) &&
+ mDirection == OUT_MESSAGE;
+ }
+
+ void Describe(int32_t* id, const char** dir, const char** sems,
+ const char** name) const
+ {
+ *id = mMessageRoutingId;
+ *dir = (IN_MESSAGE == mDirection) ? "in" : "out";
+ *sems = (INTR_SEMS == mMesageSemantics) ? "intr" :
+ (SYNC_SEMS == mMesageSemantics) ? "sync" :
+ "async";
+ *name = mMessageName;
+ }
+
+ int32_t GetRoutingId() const
+ {
+ return mMessageRoutingId;
+ }
+
+private:
+ const char* mMessageName;
+ int32_t mMessageRoutingId;
+ Semantics mMesageSemantics;
+ Direction mDirection;
+ bool mMoved;
+
+ // Disable harmful methods.
+ InterruptFrame(const InterruptFrame& aOther) = delete;
+ InterruptFrame& operator=(const InterruptFrame&) = delete;
+};
+
+class MOZ_STACK_CLASS MessageChannel::CxxStackFrame
+{
+public:
+ CxxStackFrame(MessageChannel& that, Direction direction, const Message* msg)
+ : mThat(that)
+ {
+ mThat.AssertWorkerThread();
+
+ if (mThat.mCxxStackFrames.empty())
+ mThat.EnteredCxxStack();
+
+ if (!mThat.mCxxStackFrames.append(InterruptFrame(direction, msg)))
+ MOZ_CRASH();
+
+ const InterruptFrame& frame = mThat.mCxxStackFrames.back();
+
+ if (frame.IsInterruptIncall())
+ mThat.EnteredCall();
+
+ if (frame.IsOutgoingSync())
+ mThat.EnteredSyncSend();
+
+ mThat.mSawInterruptOutMsg |= frame.IsInterruptOutcall();
+ }
+
+ ~CxxStackFrame() {
+ mThat.AssertWorkerThread();
+
+ MOZ_RELEASE_ASSERT(!mThat.mCxxStackFrames.empty());
+
+ const InterruptFrame& frame = mThat.mCxxStackFrames.back();
+ bool exitingSync = frame.IsOutgoingSync();
+ bool exitingCall = frame.IsInterruptIncall();
+ mThat.mCxxStackFrames.shrinkBy(1);
+
+ bool exitingStack = mThat.mCxxStackFrames.empty();
+
+ // According how lifetime is declared, mListener on MessageChannel
+ // lives longer than MessageChannel itself. Hence is expected to
+ // be alive. There is nothing to even assert here, there is no place
+ // we would be nullifying mListener on MessageChannel.
+
+ if (exitingCall)
+ mThat.ExitedCall();
+
+ if (exitingSync)
+ mThat.ExitedSyncSend();
+
+ if (exitingStack)
+ mThat.ExitedCxxStack();
+ }
+private:
+ MessageChannel& mThat;
+
+ // Disable harmful methods.
+ CxxStackFrame() = delete;
+ CxxStackFrame(const CxxStackFrame&) = delete;
+ CxxStackFrame& operator=(const CxxStackFrame&) = delete;
+};
+
+class AutoEnterTransaction
+{
+public:
+ explicit AutoEnterTransaction(MessageChannel *aChan,
+ int32_t aMsgSeqno,
+ int32_t aTransactionID,
+ int aNestedLevel)
+ : mChan(aChan),
+ mActive(true),
+ mOutgoing(true),
+ mNestedLevel(aNestedLevel),
+ mSeqno(aMsgSeqno),
+ mTransaction(aTransactionID),
+ mNext(mChan->mTransactionStack)
+ {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+ mChan->mTransactionStack = this;
+ }
+
+ explicit AutoEnterTransaction(MessageChannel *aChan, const IPC::Message &aMessage)
+ : mChan(aChan),
+ mActive(true),
+ mOutgoing(false),
+ mNestedLevel(aMessage.nested_level()),
+ mSeqno(aMessage.seqno()),
+ mTransaction(aMessage.transaction_id()),
+ mNext(mChan->mTransactionStack)
+ {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ if (!aMessage.is_sync()) {
+ mActive = false;
+ return;
+ }
+
+ mChan->mTransactionStack = this;
+ }
+
+ ~AutoEnterTransaction() {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+ if (mActive) {
+ mChan->mTransactionStack = mNext;
+ }
+ }
+
+ void Cancel() {
+ AutoEnterTransaction *cur = mChan->mTransactionStack;
+ MOZ_RELEASE_ASSERT(cur == this);
+ while (cur && cur->mNestedLevel != IPC::Message::NOT_NESTED) {
+ // Note that, in the following situation, we will cancel multiple
+ // transactions:
+ // 1. Parent sends NESTED_INSIDE_SYNC message P1 to child.
+ // 2. Child sends NESTED_INSIDE_SYNC message C1 to child.
+ // 3. Child dispatches P1, parent blocks.
+ // 4. Child cancels.
+ // In this case, both P1 and C1 are cancelled. The parent will
+ // remove C1 from its queue when it gets the cancellation message.
+ MOZ_RELEASE_ASSERT(cur->mActive);
+ cur->mActive = false;
+ cur = cur->mNext;
+ }
+
+ mChan->mTransactionStack = cur;
+
+ MOZ_RELEASE_ASSERT(IsComplete());
+ }
+
+ bool AwaitingSyncReply() const {
+ MOZ_RELEASE_ASSERT(mActive);
+ if (mOutgoing) {
+ return true;
+ }
+ return mNext ? mNext->AwaitingSyncReply() : false;
+ }
+
+ int AwaitingSyncReplyNestedLevel() const {
+ MOZ_RELEASE_ASSERT(mActive);
+ if (mOutgoing) {
+ return mNestedLevel;
+ }
+ return mNext ? mNext->AwaitingSyncReplyNestedLevel() : 0;
+ }
+
+ bool DispatchingSyncMessage() const {
+ MOZ_RELEASE_ASSERT(mActive);
+ if (!mOutgoing) {
+ return true;
+ }
+ return mNext ? mNext->DispatchingSyncMessage() : false;
+ }
+
+ int DispatchingSyncMessageNestedLevel() const {
+ MOZ_RELEASE_ASSERT(mActive);
+ if (!mOutgoing) {
+ return mNestedLevel;
+ }
+ return mNext ? mNext->DispatchingSyncMessageNestedLevel() : 0;
+ }
+
+ int NestedLevel() const {
+ MOZ_RELEASE_ASSERT(mActive);
+ return mNestedLevel;
+ }
+
+ int32_t SequenceNumber() const {
+ MOZ_RELEASE_ASSERT(mActive);
+ return mSeqno;
+ }
+
+ int32_t TransactionID() const {
+ MOZ_RELEASE_ASSERT(mActive);
+ return mTransaction;
+ }
+
+ void ReceivedReply(IPC::Message&& aMessage) {
+ MOZ_RELEASE_ASSERT(aMessage.seqno() == mSeqno);
+ MOZ_RELEASE_ASSERT(aMessage.transaction_id() == mTransaction);
+ MOZ_RELEASE_ASSERT(!mReply);
+ IPC_LOG("Reply received on worker thread: seqno=%d", mSeqno);
+ mReply = new IPC::Message(Move(aMessage));
+ MOZ_RELEASE_ASSERT(IsComplete());
+ }
+
+ void HandleReply(IPC::Message&& aMessage) {
+ AutoEnterTransaction *cur = mChan->mTransactionStack;
+ MOZ_RELEASE_ASSERT(cur == this);
+ while (cur) {
+ MOZ_RELEASE_ASSERT(cur->mActive);
+ if (aMessage.seqno() == cur->mSeqno) {
+ cur->ReceivedReply(Move(aMessage));
+ break;
+ }
+ cur = cur->mNext;
+ MOZ_RELEASE_ASSERT(cur);
+ }
+ }
+
+ bool IsComplete() {
+ return !mActive || mReply;
+ }
+
+ bool IsOutgoing() {
+ return mOutgoing;
+ }
+
+ bool IsCanceled() {
+ return !mActive;
+ }
+
+ bool IsBottom() const {
+ return !mNext;
+ }
+
+ bool IsError() {
+ MOZ_RELEASE_ASSERT(mReply);
+ return mReply->is_reply_error();
+ }
+
+ nsAutoPtr<IPC::Message> GetReply() {
+ return Move(mReply);
+ }
+
+private:
+ MessageChannel *mChan;
+
+ // Active is true if this transaction is on the mChan->mTransactionStack
+ // stack. Generally we're not on the stack if the transaction was canceled
+ // or if it was for a message that doesn't require transactions (an async
+ // message).
+ bool mActive;
+
+ // Is this stack frame for an outgoing message?
+ bool mOutgoing;
+
+ // Properties of the message being sent/received.
+ int mNestedLevel;
+ int32_t mSeqno;
+ int32_t mTransaction;
+
+ // Next item in mChan->mTransactionStack.
+ AutoEnterTransaction *mNext;
+
+ // Pointer the a reply received for this message, if one was received.
+ nsAutoPtr<IPC::Message> mReply;
+};
+
+MessageChannel::MessageChannel(IToplevelProtocol *aListener)
+ : mListener(aListener),
+ mChannelState(ChannelClosed),
+ mSide(UnknownSide),
+ mLink(nullptr),
+ mWorkerLoop(nullptr),
+ mChannelErrorTask(nullptr),
+ mWorkerLoopID(-1),
+ mTimeoutMs(kNoTimeout),
+ mInTimeoutSecondHalf(false),
+ mNextSeqno(0),
+ mLastSendError(SyncSendError::SendSuccess),
+ mDispatchingAsyncMessage(false),
+ mDispatchingAsyncMessageNestedLevel(0),
+ mTransactionStack(nullptr),
+ mTimedOutMessageSeqno(0),
+ mTimedOutMessageNestedLevel(0),
+ mRemoteStackDepthGuess(0),
+ mSawInterruptOutMsg(false),
+ mIsWaitingForIncoming(false),
+ mAbortOnError(false),
+ mNotifiedChannelDone(false),
+ mFlags(REQUIRE_DEFAULT),
+ mPeerPidSet(false),
+ mPeerPid(-1)
+{
+ MOZ_COUNT_CTOR(ipc::MessageChannel);
+
+#ifdef OS_WIN
+ mTopFrame = nullptr;
+ mIsSyncWaitingOnNonMainThread = false;
+#endif
+
+ mOnChannelConnectedTask =
+ NewNonOwningCancelableRunnableMethod(this, &MessageChannel::DispatchOnChannelConnected);
+
+#ifdef OS_WIN
+ mEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ MOZ_RELEASE_ASSERT(mEvent, "CreateEvent failed! Nothing is going to work!");
+#endif
+}
+
+MessageChannel::~MessageChannel()
+{
+ MOZ_COUNT_DTOR(ipc::MessageChannel);
+ IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
+#ifdef OS_WIN
+ if (mEvent) {
+ BOOL ok = CloseHandle(mEvent);
+ mEvent = nullptr;
+
+ if (!ok) {
+ gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure) <<
+ "MessageChannel failed to close. GetLastError: " <<
+ GetLastError();
+ }
+ MOZ_RELEASE_ASSERT(ok);
+ } else {
+ gfxDevCrash(mozilla::gfx::LogReason::MessageChannelCloseFailure) <<
+ "MessageChannel destructor ran without an mEvent Handle";
+ }
+#endif
+ Clear();
+}
+
+// This function returns the current transaction ID. Since the notion of a
+// "current transaction" can be hard to define when messages race with each
+// other and one gets canceled and the other doesn't, we require that this
+// function is only called when the current transaction is known to be for a
+// NESTED_INSIDE_SYNC message. In that case, we know for sure what the caller is
+// looking for.
+int32_t
+MessageChannel::CurrentNestedInsideSyncTransaction() const
+{
+ mMonitor->AssertCurrentThreadOwns();
+ if (!mTransactionStack) {
+ return 0;
+ }
+ MOZ_RELEASE_ASSERT(mTransactionStack->NestedLevel() == IPC::Message::NESTED_INSIDE_SYNC);
+ return mTransactionStack->TransactionID();
+}
+
+bool
+MessageChannel::AwaitingSyncReply() const
+{
+ mMonitor->AssertCurrentThreadOwns();
+ return mTransactionStack ? mTransactionStack->AwaitingSyncReply() : false;
+}
+
+int
+MessageChannel::AwaitingSyncReplyNestedLevel() const
+{
+ mMonitor->AssertCurrentThreadOwns();
+ return mTransactionStack ? mTransactionStack->AwaitingSyncReplyNestedLevel() : 0;
+}
+
+bool
+MessageChannel::DispatchingSyncMessage() const
+{
+ mMonitor->AssertCurrentThreadOwns();
+ return mTransactionStack ? mTransactionStack->DispatchingSyncMessage() : false;
+}
+
+int
+MessageChannel::DispatchingSyncMessageNestedLevel() const
+{
+ mMonitor->AssertCurrentThreadOwns();
+ return mTransactionStack ? mTransactionStack->DispatchingSyncMessageNestedLevel() : 0;
+}
+
+static void
+PrintErrorMessage(Side side, const char* channelName, const char* msg)
+{
+ const char *from = (side == ChildSide)
+ ? "Child"
+ : ((side == ParentSide) ? "Parent" : "Unknown");
+ printf_stderr("\n###!!! [%s][%s] Error: %s\n\n", from, channelName, msg);
+}
+
+bool
+MessageChannel::Connected() const
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ // The transport layer allows us to send messages before
+ // receiving the "connected" ack from the remote side.
+ return (ChannelOpening == mChannelState || ChannelConnected == mChannelState);
+}
+
+bool
+MessageChannel::CanSend() const
+{
+ if (!mMonitor) {
+ return false;
+ }
+ MonitorAutoLock lock(*mMonitor);
+ return Connected();
+}
+
+void
+MessageChannel::Clear()
+{
+ // Don't clear mWorkerLoopID; we use it in AssertLinkThread() and
+ // AssertWorkerThread().
+ //
+ // Also don't clear mListener. If we clear it, then sending a message
+ // through this channel after it's Clear()'ed can cause this process to
+ // crash.
+ //
+ // In practice, mListener owns the channel, so the channel gets deleted
+ // before mListener. But just to be safe, mListener is a weak pointer.
+
+ if (gParentProcessBlocker == this) {
+ gParentProcessBlocker = nullptr;
+ }
+
+ mWorkerLoop = nullptr;
+ delete mLink;
+ mLink = nullptr;
+
+ mOnChannelConnectedTask->Cancel();
+
+ if (mChannelErrorTask) {
+ mChannelErrorTask->Cancel();
+ mChannelErrorTask = nullptr;
+ }
+
+ // Free up any memory used by pending messages.
+ for (RefPtr<MessageTask> task : mPending) {
+ task->Clear();
+ }
+ mPending.clear();
+
+ mOutOfTurnReplies.clear();
+ while (!mDeferred.empty()) {
+ mDeferred.pop();
+ }
+}
+
+bool
+MessageChannel::Open(Transport* aTransport, MessageLoop* aIOLoop, Side aSide)
+{
+ NS_PRECONDITION(!mLink, "Open() called > once");
+
+ mMonitor = new RefCountedMonitor();
+ mWorkerLoop = MessageLoop::current();
+ mWorkerLoopID = mWorkerLoop->id();
+
+ ProcessLink *link = new ProcessLink(this);
+ link->Open(aTransport, aIOLoop, aSide); // :TODO: n.b.: sets mChild
+ mLink = link;
+ return true;
+}
+
+bool
+MessageChannel::Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side aSide)
+{
+ // Opens a connection to another thread in the same process.
+
+ // This handshake proceeds as follows:
+ // - Let A be the thread initiating the process (either child or parent)
+ // and B be the other thread.
+ // - A spawns thread for B, obtaining B's message loop
+ // - A creates ProtocolChild and ProtocolParent instances.
+ // Let PA be the one appropriate to A and PB the side for B.
+ // - A invokes PA->Open(PB, ...):
+ // - set state to mChannelOpening
+ // - this will place a work item in B's worker loop (see next bullet)
+ // and then spins until PB->mChannelState becomes mChannelConnected
+ // - meanwhile, on PB's worker loop, the work item is removed and:
+ // - invokes PB->SlaveOpen(PA, ...):
+ // - sets its state and that of PA to Connected
+ NS_PRECONDITION(aTargetChan, "Need a target channel");
+ NS_PRECONDITION(ChannelClosed == mChannelState, "Not currently closed");
+
+ CommonThreadOpenInit(aTargetChan, aSide);
+
+ Side oppSide = UnknownSide;
+ switch(aSide) {
+ case ChildSide: oppSide = ParentSide; break;
+ case ParentSide: oppSide = ChildSide; break;
+ case UnknownSide: break;
+ }
+
+ mMonitor = new RefCountedMonitor();
+
+ MonitorAutoLock lock(*mMonitor);
+ mChannelState = ChannelOpening;
+ aTargetLoop->PostTask(NewNonOwningRunnableMethod
+ <MessageChannel*, Side>(aTargetChan,
+ &MessageChannel::OnOpenAsSlave,
+ this, oppSide));
+
+ while (ChannelOpening == mChannelState)
+ mMonitor->Wait();
+ MOZ_RELEASE_ASSERT(ChannelConnected == mChannelState, "not connected when awoken");
+ return (ChannelConnected == mChannelState);
+}
+
+void
+MessageChannel::OnOpenAsSlave(MessageChannel *aTargetChan, Side aSide)
+{
+ // Invoked when the other side has begun the open.
+ NS_PRECONDITION(ChannelClosed == mChannelState,
+ "Not currently closed");
+ NS_PRECONDITION(ChannelOpening == aTargetChan->mChannelState,
+ "Target channel not in the process of opening");
+
+ CommonThreadOpenInit(aTargetChan, aSide);
+ mMonitor = aTargetChan->mMonitor;
+
+ MonitorAutoLock lock(*mMonitor);
+ MOZ_RELEASE_ASSERT(ChannelOpening == aTargetChan->mChannelState,
+ "Target channel not in the process of opening");
+ mChannelState = ChannelConnected;
+ aTargetChan->mChannelState = ChannelConnected;
+ aTargetChan->mMonitor->Notify();
+}
+
+void
+MessageChannel::CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide)
+{
+ mWorkerLoop = MessageLoop::current();
+ mWorkerLoopID = mWorkerLoop->id();
+ mLink = new ThreadLink(this, aTargetChan);
+ mSide = aSide;
+}
+
+bool
+MessageChannel::Echo(Message* aMsg)
+{
+ nsAutoPtr<Message> msg(aMsg);
+ AssertWorkerThread();
+ mMonitor->AssertNotCurrentThreadOwns();
+ if (MSG_ROUTING_NONE == msg->routing_id()) {
+ ReportMessageRouteError("MessageChannel::Echo");
+ return false;
+ }
+
+ MonitorAutoLock lock(*mMonitor);
+
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel", msg);
+ return false;
+ }
+
+ mLink->EchoMessage(msg.forget());
+ return true;
+}
+
+bool
+MessageChannel::Send(Message* aMsg)
+{
+ if (aMsg->size() >= kMinTelemetryMessageSize) {
+ Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
+ nsDependentCString(aMsg->name()), aMsg->size());
+ }
+
+ MOZ_RELEASE_ASSERT(!aMsg->is_sync());
+ MOZ_RELEASE_ASSERT(aMsg->nested_level() != IPC::Message::NESTED_INSIDE_SYNC);
+
+ CxxStackFrame frame(*this, OUT_MESSAGE, aMsg);
+
+ nsAutoPtr<Message> msg(aMsg);
+ AssertWorkerThread();
+ mMonitor->AssertNotCurrentThreadOwns();
+ if (MSG_ROUTING_NONE == msg->routing_id()) {
+ ReportMessageRouteError("MessageChannel::Send");
+ return false;
+ }
+
+ MonitorAutoLock lock(*mMonitor);
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel", msg);
+ return false;
+ }
+ mLink->SendMessage(msg.forget());
+ return true;
+}
+
+class CancelMessage : public IPC::Message
+{
+public:
+ explicit CancelMessage(int transaction) :
+ IPC::Message(MSG_ROUTING_NONE, CANCEL_MESSAGE_TYPE)
+ {
+ set_transaction_id(transaction);
+ }
+ static bool Read(const Message* msg) {
+ return true;
+ }
+ void Log(const std::string& aPrefix, FILE* aOutf) const {
+ fputs("(special `Cancel' message)", aOutf);
+ }
+};
+
+bool
+MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg)
+{
+ AssertLinkThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ if (MSG_ROUTING_NONE == aMsg.routing_id()) {
+ if (GOODBYE_MESSAGE_TYPE == aMsg.type()) {
+ // :TODO: Sort out Close() on this side racing with Close() on the
+ // other side
+ mChannelState = ChannelClosing;
+ if (LoggingEnabled()) {
+ printf("NOTE: %s process received `Goodbye', closing down\n",
+ (mSide == ChildSide) ? "child" : "parent");
+ }
+ return true;
+ } else if (CANCEL_MESSAGE_TYPE == aMsg.type()) {
+ IPC_LOG("Cancel from message");
+ CancelTransaction(aMsg.transaction_id());
+ NotifyWorkerThread();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+MessageChannel::ShouldDeferMessage(const Message& aMsg)
+{
+ // Never defer messages that have the highest nested level, even async
+ // ones. This is safe because only the child can send these messages, so
+ // they can never nest.
+ if (aMsg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW)
+ return false;
+
+ // Unless they're NESTED_INSIDE_CPOW, we always defer async messages.
+ // Note that we never send an async NESTED_INSIDE_SYNC message.
+ if (!aMsg.is_sync()) {
+ MOZ_RELEASE_ASSERT(aMsg.nested_level() == IPC::Message::NOT_NESTED);
+ return true;
+ }
+
+ int msgNestedLevel = aMsg.nested_level();
+ int waitingNestedLevel = AwaitingSyncReplyNestedLevel();
+
+ // Always defer if the nested level of the incoming message is less than the
+ // nested level of the message we're awaiting.
+ if (msgNestedLevel < waitingNestedLevel)
+ return true;
+
+ // Never defer if the message has strictly greater nested level.
+ if (msgNestedLevel > waitingNestedLevel)
+ return false;
+
+ // When both sides send sync messages of the same nested level, we resolve the
+ // race by dispatching in the child and deferring the incoming message in
+ // the parent. However, the parent still needs to dispatch nested sync
+ // messages.
+ //
+ // Deferring in the parent only sort of breaks message ordering. When the
+ // child's message comes in, we can pretend the child hasn't quite
+ // finished sending it yet. Since the message is sync, we know that the
+ // child hasn't moved on yet.
+ return mSide == ParentSide && aMsg.transaction_id() != CurrentNestedInsideSyncTransaction();
+}
+
+void
+MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
+{
+ AssertLinkThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ if (MaybeInterceptSpecialIOMessage(aMsg))
+ return;
+
+ // Regardless of the Interrupt stack, if we're awaiting a sync reply,
+ // we know that it needs to be immediately handled to unblock us.
+ if (aMsg.is_sync() && aMsg.is_reply()) {
+ IPC_LOG("Received reply seqno=%d xid=%d", aMsg.seqno(), aMsg.transaction_id());
+
+ if (aMsg.seqno() == mTimedOutMessageSeqno) {
+ // Drop the message, but allow future sync messages to be sent.
+ IPC_LOG("Received reply to timedout message; igoring; xid=%d", mTimedOutMessageSeqno);
+ EndTimeout();
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(AwaitingSyncReply());
+ MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
+
+ mTransactionStack->HandleReply(Move(aMsg));
+ NotifyWorkerThread();
+ return;
+ }
+
+ // Nested messages cannot be compressed.
+ MOZ_RELEASE_ASSERT(aMsg.compress_type() == IPC::Message::COMPRESSION_NONE ||
+ aMsg.nested_level() == IPC::Message::NOT_NESTED);
+
+ bool reuseTask = false;
+ if (aMsg.compress_type() == IPC::Message::COMPRESSION_ENABLED) {
+ bool compress = (!mPending.isEmpty() &&
+ mPending.getLast()->Msg().type() == aMsg.type() &&
+ mPending.getLast()->Msg().routing_id() == aMsg.routing_id());
+ if (compress) {
+ // This message type has compression enabled, and the back of the
+ // queue was the same message type and routed to the same destination.
+ // Replace it with the newer message.
+ MOZ_RELEASE_ASSERT(mPending.getLast()->Msg().compress_type() ==
+ IPC::Message::COMPRESSION_ENABLED);
+ mPending.getLast()->Msg() = Move(aMsg);
+
+ reuseTask = true;
+ }
+ } else if (aMsg.compress_type() == IPC::Message::COMPRESSION_ALL && !mPending.isEmpty()) {
+ for (RefPtr<MessageTask> p = mPending.getLast(); p; p = p->getPrevious()) {
+ if (p->Msg().type() == aMsg.type() &&
+ p->Msg().routing_id() == aMsg.routing_id())
+ {
+ // This message type has compression enabled, and the queue
+ // holds a message with the same message type and routed to the
+ // same destination. Erase it. Note that, since we always
+ // compress these redundancies, There Can Be Only One.
+ MOZ_RELEASE_ASSERT(p->Msg().compress_type() == IPC::Message::COMPRESSION_ALL);
+ p->remove();
+ break;
+ }
+ }
+ }
+
+ bool wakeUpSyncSend = AwaitingSyncReply() && !ShouldDeferMessage(aMsg);
+
+ bool shouldWakeUp = AwaitingInterruptReply() ||
+ wakeUpSyncSend ||
+ AwaitingIncomingMessage();
+
+ // Although we usually don't need to post a message task if
+ // shouldWakeUp is true, it's easier to post anyway than to have to
+ // guarantee that every Send call processes everything it's supposed to
+ // before returning.
+ bool shouldPostTask = !shouldWakeUp || wakeUpSyncSend;
+
+ IPC_LOG("Receive on link thread; seqno=%d, xid=%d, shouldWakeUp=%d",
+ aMsg.seqno(), aMsg.transaction_id(), shouldWakeUp);
+
+ if (reuseTask) {
+ return;
+ }
+
+ // There are three cases we're concerned about, relating to the state of the
+ // main thread:
+ //
+ // (1) We are waiting on a sync reply - main thread is blocked on the
+ // IPC monitor.
+ // - If the message is NESTED_INSIDE_SYNC, we wake up the main thread to
+ // deliver the message depending on ShouldDeferMessage. Otherwise, we
+ // leave it in the mPending queue, posting a task to the main event
+ // loop, where it will be processed once the synchronous reply has been
+ // received.
+ //
+ // (2) We are waiting on an Interrupt reply - main thread is blocked on the
+ // IPC monitor.
+ // - Always notify and wake up the main thread.
+ //
+ // (3) We are not waiting on a reply.
+ // - We post a task to the main event loop.
+ //
+ // Note that, we may notify the main thread even though the monitor is not
+ // blocked. This is okay, since we always check for pending events before
+ // blocking again.
+
+ RefPtr<MessageTask> task = new MessageTask(this, Move(aMsg));
+ mPending.insertBack(task);
+
+ if (shouldWakeUp) {
+ NotifyWorkerThread();
+ }
+
+ if (shouldPostTask) {
+ task->Post();
+ }
+}
+
+void
+MessageChannel::PeekMessages(mozilla::function<bool(const Message& aMsg)> aInvoke)
+{
+ // FIXME: We shouldn't be holding the lock for aInvoke!
+ MonitorAutoLock lock(*mMonitor);
+
+ for (RefPtr<MessageTask> it : mPending) {
+ const Message &msg = it->Msg();
+ if (!aInvoke(msg)) {
+ break;
+ }
+ }
+}
+
+void
+MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ IPC_LOG("ProcessPendingRequests for seqno=%d, xid=%d",
+ aTransaction.SequenceNumber(), aTransaction.TransactionID());
+
+ // Loop until there aren't any more nested messages to process.
+ for (;;) {
+ // If we canceled during ProcessPendingRequest, then we need to leave
+ // immediately because the results of ShouldDeferMessage will be
+ // operating with weird state (as if no Send is in progress). That could
+ // cause even NOT_NESTED sync messages to be processed (but not
+ // NOT_NESTED async messages), which would break message ordering.
+ if (aTransaction.IsCanceled()) {
+ return;
+ }
+
+ mozilla::Vector<Message> toProcess;
+
+ for (RefPtr<MessageTask> p = mPending.getFirst(); p; ) {
+ Message &msg = p->Msg();
+
+ MOZ_RELEASE_ASSERT(!aTransaction.IsCanceled(),
+ "Calling ShouldDeferMessage when cancelled");
+ bool defer = ShouldDeferMessage(msg);
+
+ // Only log the interesting messages.
+ if (msg.is_sync() || msg.nested_level() == IPC::Message::NESTED_INSIDE_CPOW) {
+ IPC_LOG("ShouldDeferMessage(seqno=%d) = %d", msg.seqno(), defer);
+ }
+
+ if (!defer) {
+ if (!toProcess.append(Move(msg)))
+ MOZ_CRASH();
+
+ p = p->removeAndGetNext();
+ continue;
+ }
+ p = p->getNext();
+ }
+
+ if (toProcess.empty()) {
+ break;
+ }
+
+ // Processing these messages could result in more messages, so we
+ // loop around to check for more afterwards.
+
+ for (auto it = toProcess.begin(); it != toProcess.end(); it++) {
+ ProcessPendingRequest(Move(*it));
+ }
+ }
+}
+
+bool
+MessageChannel::Send(Message* aMsg, Message* aReply)
+{
+ if (aMsg->size() >= kMinTelemetryMessageSize) {
+ Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
+ nsDependentCString(aMsg->name()), aMsg->size());
+ }
+
+ nsAutoPtr<Message> msg(aMsg);
+
+ // Sanity checks.
+ AssertWorkerThread();
+ mMonitor->AssertNotCurrentThreadOwns();
+
+#ifdef OS_WIN
+ SyncStackFrame frame(this, false);
+ NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+#endif
+
+ CxxStackFrame f(*this, OUT_MESSAGE, msg);
+
+ MonitorAutoLock lock(*mMonitor);
+
+ if (mTimedOutMessageSeqno) {
+ // Don't bother sending another sync message if a previous one timed out
+ // and we haven't received a reply for it. Once the original timed-out
+ // message receives a reply, we'll be able to send more sync messages
+ // again.
+ IPC_LOG("Send() failed due to previous timeout");
+ mLastSendError = SyncSendError::PreviousTimeout;
+ return false;
+ }
+
+ if (DispatchingSyncMessageNestedLevel() == IPC::Message::NOT_NESTED &&
+ msg->nested_level() > IPC::Message::NOT_NESTED)
+ {
+ // Don't allow sending CPOWs while we're dispatching a sync message.
+ // If you want to do that, use sendRpcMessage instead.
+ IPC_LOG("Nested level forbids send");
+ mLastSendError = SyncSendError::SendingCPOWWhileDispatchingSync;
+ return false;
+ }
+
+ if (DispatchingSyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW ||
+ DispatchingAsyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW)
+ {
+ // Generally only the parent dispatches urgent messages. And the only
+ // sync messages it can send are NESTED_INSIDE_SYNC. Mainly we want to ensure
+ // here that we don't return false for non-CPOW messages.
+ MOZ_RELEASE_ASSERT(msg->nested_level() == IPC::Message::NESTED_INSIDE_SYNC);
+ IPC_LOG("Sending while dispatching urgent message");
+ mLastSendError = SyncSendError::SendingCPOWWhileDispatchingUrgent;
+ return false;
+ }
+
+ if (msg->nested_level() < DispatchingSyncMessageNestedLevel() ||
+ msg->nested_level() < AwaitingSyncReplyNestedLevel())
+ {
+ MOZ_RELEASE_ASSERT(DispatchingSyncMessage() || DispatchingAsyncMessage());
+ IPC_LOG("Cancel from Send");
+ CancelMessage *cancel = new CancelMessage(CurrentNestedInsideSyncTransaction());
+ CancelTransaction(CurrentNestedInsideSyncTransaction());
+ mLink->SendMessage(cancel);
+ }
+
+ IPC_ASSERT(msg->is_sync(), "can only Send() sync messages here");
+
+ IPC_ASSERT(msg->nested_level() >= DispatchingSyncMessageNestedLevel(),
+ "can't send sync message of a lesser nested level than what's being dispatched");
+ IPC_ASSERT(AwaitingSyncReplyNestedLevel() <= msg->nested_level(),
+ "nested sync message sends must be of increasing nested level");
+ IPC_ASSERT(DispatchingSyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW,
+ "not allowed to send messages while dispatching urgent messages");
+
+ IPC_ASSERT(DispatchingAsyncMessageNestedLevel() != IPC::Message::NESTED_INSIDE_CPOW,
+ "not allowed to send messages while dispatching urgent messages");
+
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::SendAndWait", msg);
+ mLastSendError = SyncSendError::NotConnectedBeforeSend;
+ return false;
+ }
+
+ msg->set_seqno(NextSeqno());
+
+ int32_t seqno = msg->seqno();
+ int nestedLevel = msg->nested_level();
+ msgid_t replyType = msg->type() + 1;
+
+ AutoEnterTransaction *stackTop = mTransactionStack;
+
+ // If the most recent message on the stack is NESTED_INSIDE_SYNC, then our
+ // message should nest inside that and we use the same transaction
+ // ID. Otherwise we need a new transaction ID (so we use the seqno of the
+ // message we're sending).
+ bool nest = stackTop && stackTop->NestedLevel() == IPC::Message::NESTED_INSIDE_SYNC;
+ int32_t transaction = nest ? stackTop->TransactionID() : seqno;
+ msg->set_transaction_id(transaction);
+
+ bool handleWindowsMessages = mListener->HandleWindowsMessages(*aMsg);
+ AutoEnterTransaction transact(this, seqno, transaction, nestedLevel);
+
+ IPC_LOG("Send seqno=%d, xid=%d", seqno, transaction);
+
+ // msg will be destroyed soon, but name() is not owned by msg.
+ const char* msgName = msg->name();
+
+ mLink->SendMessage(msg.forget());
+
+ while (true) {
+ MOZ_RELEASE_ASSERT(!transact.IsCanceled());
+ ProcessPendingRequests(transact);
+ if (transact.IsComplete()) {
+ break;
+ }
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::Send");
+ mLastSendError = SyncSendError::DisconnectedDuringSend;
+ return false;
+ }
+
+ MOZ_RELEASE_ASSERT(!mTimedOutMessageSeqno);
+ MOZ_RELEASE_ASSERT(!transact.IsComplete());
+ MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
+
+ bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages);
+
+ if (mListener->NeedArtificialSleep()) {
+ MonitorAutoUnlock unlock(*mMonitor);
+ mListener->ArtificialSleep();
+ }
+
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::SendAndWait");
+ mLastSendError = SyncSendError::DisconnectedDuringSend;
+ return false;
+ }
+
+ if (transact.IsCanceled()) {
+ break;
+ }
+
+ MOZ_RELEASE_ASSERT(mTransactionStack == &transact);
+
+ // We only time out a message if it initiated a new transaction (i.e.,
+ // if neither side has any other message Sends on the stack).
+ bool canTimeOut = transact.IsBottom();
+ if (maybeTimedOut && canTimeOut && !ShouldContinueFromTimeout()) {
+ // Since ShouldContinueFromTimeout drops the lock, we need to
+ // re-check all our conditions here. We shouldn't time out if any of
+ // these things happen because there won't be a reply to the timed
+ // out message in these cases.
+ if (transact.IsComplete()) {
+ break;
+ }
+
+ IPC_LOG("Timing out Send: xid=%d", transaction);
+
+ mTimedOutMessageSeqno = seqno;
+ mTimedOutMessageNestedLevel = nestedLevel;
+ mLastSendError = SyncSendError::TimedOut;
+ return false;
+ }
+
+ if (transact.IsCanceled()) {
+ break;
+ }
+ }
+
+ if (transact.IsCanceled()) {
+ IPC_LOG("Other side canceled seqno=%d, xid=%d", seqno, transaction);
+ mLastSendError = SyncSendError::CancelledAfterSend;
+ return false;
+ }
+
+ if (transact.IsError()) {
+ IPC_LOG("Error: seqno=%d, xid=%d", seqno, transaction);
+ mLastSendError = SyncSendError::ReplyError;
+ return false;
+ }
+
+ IPC_LOG("Got reply: seqno=%d, xid=%d", seqno, transaction);
+
+ nsAutoPtr<Message> reply = transact.GetReply();
+
+ MOZ_RELEASE_ASSERT(reply);
+ MOZ_RELEASE_ASSERT(reply->is_reply(), "expected reply");
+ MOZ_RELEASE_ASSERT(!reply->is_reply_error());
+ MOZ_RELEASE_ASSERT(reply->seqno() == seqno);
+ MOZ_RELEASE_ASSERT(reply->type() == replyType, "wrong reply type");
+ MOZ_RELEASE_ASSERT(reply->is_sync());
+
+ *aReply = Move(*reply);
+ if (aReply->size() >= kMinTelemetryMessageSize) {
+ Telemetry::Accumulate(Telemetry::IPC_REPLY_SIZE,
+ nsDependentCString(msgName), aReply->size());
+ }
+ return true;
+}
+
+bool
+MessageChannel::Call(Message* aMsg, Message* aReply)
+{
+ nsAutoPtr<Message> msg(aMsg);
+ AssertWorkerThread();
+ mMonitor->AssertNotCurrentThreadOwns();
+
+#ifdef OS_WIN
+ SyncStackFrame frame(this, true);
+#endif
+
+ // This must come before MonitorAutoLock, as its destructor acquires the
+ // monitor lock.
+ CxxStackFrame cxxframe(*this, OUT_MESSAGE, msg);
+
+ MonitorAutoLock lock(*mMonitor);
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::Call", msg);
+ return false;
+ }
+
+ // Sanity checks.
+ IPC_ASSERT(!AwaitingSyncReply(),
+ "cannot issue Interrupt call while blocked on sync request");
+ IPC_ASSERT(!DispatchingSyncMessage(),
+ "violation of sync handler invariant");
+ IPC_ASSERT(msg->is_interrupt(), "can only Call() Interrupt messages here");
+
+ msg->set_seqno(NextSeqno());
+ msg->set_interrupt_remote_stack_depth_guess(mRemoteStackDepthGuess);
+ msg->set_interrupt_local_stack_depth(1 + InterruptStackDepth());
+ mInterruptStack.push(MessageInfo(*msg));
+ mLink->SendMessage(msg.forget());
+
+ while (true) {
+ // if a handler invoked by *Dispatch*() spun a nested event
+ // loop, and the connection was broken during that loop, we
+ // might have already processed the OnError event. if so,
+ // trying another loop iteration will be futile because
+ // channel state will have been cleared
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::Call");
+ return false;
+ }
+
+#ifdef OS_WIN
+ // We need to limit the scoped of neuteredRgn to this spot in the code.
+ // Window neutering can't be enabled during some plugin calls because
+ // we then risk the neutered window procedure being subclassed by a
+ // plugin.
+ {
+ NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+ /* We should pump messages at this point to ensure that the IPC peer
+ does not become deadlocked on a pending inter-thread SendMessage() */
+ neuteredRgn.PumpOnce();
+ }
+#endif
+
+ // Now might be the time to process a message deferred because of race
+ // resolution.
+ MaybeUndeferIncall();
+
+ // Wait for an event to occur.
+ while (!InterruptEventOccurred()) {
+ bool maybeTimedOut = !WaitForInterruptNotify();
+
+ // We might have received a "subtly deferred" message in a nested
+ // loop that it's now time to process.
+ if (InterruptEventOccurred() ||
+ (!maybeTimedOut && (!mDeferred.empty() || !mOutOfTurnReplies.empty())))
+ {
+ break;
+ }
+
+ if (maybeTimedOut && !ShouldContinueFromTimeout())
+ return false;
+ }
+
+ Message recvd;
+ MessageMap::iterator it;
+
+ if ((it = mOutOfTurnReplies.find(mInterruptStack.top().seqno()))
+ != mOutOfTurnReplies.end())
+ {
+ recvd = Move(it->second);
+ mOutOfTurnReplies.erase(it);
+ } else if (!mPending.isEmpty()) {
+ RefPtr<MessageTask> task = mPending.popFirst();
+ recvd = Move(task->Msg());
+ } else {
+ // because of subtleties with nested event loops, it's possible
+ // that we got here and nothing happened. or, we might have a
+ // deferred in-call that needs to be processed. either way, we
+ // won't break the inner while loop again until something new
+ // happens.
+ continue;
+ }
+
+ // If the message is not Interrupt, we can dispatch it as normal.
+ if (!recvd.is_interrupt()) {
+ DispatchMessage(Move(recvd));
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::DispatchMessage");
+ return false;
+ }
+ continue;
+ }
+
+ // If the message is an Interrupt reply, either process it as a reply to our
+ // call, or add it to the list of out-of-turn replies we've received.
+ if (recvd.is_reply()) {
+ IPC_ASSERT(!mInterruptStack.empty(), "invalid Interrupt stack");
+
+ // If this is not a reply the call we've initiated, add it to our
+ // out-of-turn replies and keep polling for events.
+ {
+ const MessageInfo &outcall = mInterruptStack.top();
+
+ // Note, In the parent, sequence numbers increase from 0, and
+ // in the child, they decrease from 0.
+ if ((mSide == ChildSide && recvd.seqno() > outcall.seqno()) ||
+ (mSide != ChildSide && recvd.seqno() < outcall.seqno()))
+ {
+ mOutOfTurnReplies[recvd.seqno()] = Move(recvd);
+ continue;
+ }
+
+ IPC_ASSERT(recvd.is_reply_error() ||
+ (recvd.type() == (outcall.type() + 1) &&
+ recvd.seqno() == outcall.seqno()),
+ "somebody's misbehavin'", true);
+ }
+
+ // We received a reply to our most recent outstanding call. Pop
+ // this frame and return the reply.
+ mInterruptStack.pop();
+
+ bool is_reply_error = recvd.is_reply_error();
+ if (!is_reply_error) {
+ *aReply = Move(recvd);
+ }
+
+ // If we have no more pending out calls waiting on replies, then
+ // the reply queue should be empty.
+ IPC_ASSERT(!mInterruptStack.empty() || mOutOfTurnReplies.empty(),
+ "still have pending replies with no pending out-calls",
+ true);
+
+ return !is_reply_error;
+ }
+
+ // Dispatch an Interrupt in-call. Snapshot the current stack depth while we
+ // own the monitor.
+ size_t stackDepth = InterruptStackDepth();
+ {
+ MonitorAutoUnlock unlock(*mMonitor);
+
+ CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
+ DispatchInterruptMessage(Move(recvd), stackDepth);
+ }
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::DispatchInterruptMessage");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+MessageChannel::WaitForIncomingMessage()
+{
+#ifdef OS_WIN
+ SyncStackFrame frame(this, true);
+ NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+#endif
+
+ MonitorAutoLock lock(*mMonitor);
+ AutoEnterWaitForIncoming waitingForIncoming(*this);
+ if (mChannelState != ChannelConnected) {
+ return false;
+ }
+ if (!HasPendingEvents()) {
+ return WaitForInterruptNotify();
+ }
+
+ MOZ_RELEASE_ASSERT(!mPending.isEmpty());
+ RefPtr<MessageTask> task = mPending.getFirst();
+ RunMessage(*task);
+ return true;
+}
+
+bool
+MessageChannel::HasPendingEvents()
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+ return Connected() && !mPending.isEmpty();
+}
+
+bool
+MessageChannel::InterruptEventOccurred()
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+ IPC_ASSERT(InterruptStackDepth() > 0, "not in wait loop");
+
+ return (!Connected() ||
+ !mPending.isEmpty() ||
+ (!mOutOfTurnReplies.empty() &&
+ mOutOfTurnReplies.find(mInterruptStack.top().seqno()) !=
+ mOutOfTurnReplies.end()));
+}
+
+bool
+MessageChannel::ProcessPendingRequest(Message &&aUrgent)
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ IPC_LOG("Process pending: seqno=%d, xid=%d", aUrgent.seqno(), aUrgent.transaction_id());
+
+ DispatchMessage(Move(aUrgent));
+ if (!Connected()) {
+ ReportConnectionError("MessageChannel::ProcessPendingRequest");
+ return false;
+ }
+
+ return true;
+}
+
+bool
+MessageChannel::ShouldRunMessage(const Message& aMsg)
+{
+ if (!mTimedOutMessageSeqno) {
+ return true;
+ }
+
+ // If we've timed out a message and we're awaiting the reply to the timed
+ // out message, we have to be careful what messages we process. Here's what
+ // can go wrong:
+ // 1. child sends a NOT_NESTED sync message S
+ // 2. parent sends a NESTED_INSIDE_SYNC sync message H at the same time
+ // 3. parent times out H
+ // 4. child starts processing H and sends a NESTED_INSIDE_SYNC message H' nested
+ // within the same transaction
+ // 5. parent dispatches S and sends reply
+ // 6. child asserts because it instead expected a reply to H'.
+ //
+ // To solve this, we refuse to process S in the parent until we get a reply
+ // to H. More generally, let the timed out message be M. We don't process a
+ // message unless the child would need the response to that message in order
+ // to process M. Those messages are the ones that have a higher nested level
+ // than M or that are part of the same transaction as M.
+ if (aMsg.nested_level() < mTimedOutMessageNestedLevel ||
+ (aMsg.nested_level() == mTimedOutMessageNestedLevel
+ && aMsg.transaction_id() != mTimedOutMessageSeqno))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void
+MessageChannel::RunMessage(MessageTask& aTask)
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ Message& msg = aTask.Msg();
+
+ if (!Connected()) {
+ ReportConnectionError("RunMessage");
+ return;
+ }
+
+ // Check that we're going to run the first message that's valid to run.
+#ifdef DEBUG
+ for (RefPtr<MessageTask> task : mPending) {
+ if (task == &aTask) {
+ break;
+ }
+
+ MOZ_ASSERT(!ShouldRunMessage(task->Msg()) ||
+ aTask.Msg().priority() != task->Msg().priority());
+
+ }
+#endif
+
+ if (!mDeferred.empty()) {
+ MaybeUndeferIncall();
+ }
+
+ if (!ShouldRunMessage(msg)) {
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(aTask.isInList());
+ aTask.remove();
+
+ if (IsOnCxxStack() && msg.is_interrupt() && msg.is_reply()) {
+ // We probably just received a reply in a nested loop for an
+ // Interrupt call sent before entering that loop.
+ mOutOfTurnReplies[msg.seqno()] = Move(msg);
+ return;
+ }
+
+ DispatchMessage(Move(msg));
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(MessageChannel::MessageTask, CancelableRunnable, nsIRunnablePriority)
+
+nsresult
+MessageChannel::MessageTask::Run()
+{
+ if (!mChannel) {
+ return NS_OK;
+ }
+
+ mChannel->AssertWorkerThread();
+ mChannel->mMonitor->AssertNotCurrentThreadOwns();
+
+ MonitorAutoLock lock(*mChannel->mMonitor);
+
+ // In case we choose not to run this message, we may need to be able to Post
+ // it again.
+ mScheduled = false;
+
+ if (!isInList()) {
+ return NS_OK;
+ }
+
+ mChannel->RunMessage(*this);
+ return NS_OK;
+}
+
+// Warning: This method removes the receiver from whatever list it might be in.
+nsresult
+MessageChannel::MessageTask::Cancel()
+{
+ if (!mChannel) {
+ return NS_OK;
+ }
+
+ mChannel->AssertWorkerThread();
+ mChannel->mMonitor->AssertNotCurrentThreadOwns();
+
+ MonitorAutoLock lock(*mChannel->mMonitor);
+
+ if (!isInList()) {
+ return NS_OK;
+ }
+ remove();
+
+ return NS_OK;
+}
+
+void
+MessageChannel::MessageTask::Post()
+{
+ MOZ_RELEASE_ASSERT(!mScheduled);
+ MOZ_RELEASE_ASSERT(isInList());
+
+ mScheduled = true;
+
+ RefPtr<MessageTask> self = this;
+ mChannel->mWorkerLoop->PostTask(self.forget());
+}
+
+void
+MessageChannel::MessageTask::Clear()
+{
+ mChannel->AssertWorkerThread();
+
+ mChannel = nullptr;
+}
+
+NS_IMETHODIMP
+MessageChannel::MessageTask::GetPriority(uint32_t* aPriority)
+{
+ *aPriority = mMessage.priority() == Message::HIGH_PRIORITY ?
+ PRIORITY_HIGH : PRIORITY_NORMAL;
+ return NS_OK;
+}
+
+void
+MessageChannel::DispatchMessage(Message &&aMsg)
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ Maybe<AutoNoJSAPI> nojsapi;
+ if (ScriptSettingsInitialized() && NS_IsMainThread())
+ nojsapi.emplace();
+
+ nsAutoPtr<Message> reply;
+
+ IPC_LOG("DispatchMessage: seqno=%d, xid=%d", aMsg.seqno(), aMsg.transaction_id());
+
+ {
+ AutoEnterTransaction transaction(this, aMsg);
+
+ int id = aMsg.transaction_id();
+ MOZ_RELEASE_ASSERT(!aMsg.is_sync() || id == transaction.TransactionID());
+
+ {
+ MonitorAutoUnlock unlock(*mMonitor);
+ CxxStackFrame frame(*this, IN_MESSAGE, &aMsg);
+
+ mListener->ArtificialSleep();
+
+ if (aMsg.is_sync())
+ DispatchSyncMessage(aMsg, *getter_Transfers(reply));
+ else if (aMsg.is_interrupt())
+ DispatchInterruptMessage(Move(aMsg), 0);
+ else
+ DispatchAsyncMessage(aMsg);
+
+ mListener->ArtificialSleep();
+ }
+
+ if (reply && transaction.IsCanceled()) {
+ // The transaction has been canceled. Don't send a reply.
+ IPC_LOG("Nulling out reply due to cancellation, seqno=%d, xid=%d", aMsg.seqno(), id);
+ reply = nullptr;
+ }
+ }
+
+ if (reply && ChannelConnected == mChannelState) {
+ IPC_LOG("Sending reply seqno=%d, xid=%d", aMsg.seqno(), aMsg.transaction_id());
+ mLink->SendMessage(reply.forget());
+ }
+}
+
+void
+MessageChannel::DispatchSyncMessage(const Message& aMsg, Message*& aReply)
+{
+ AssertWorkerThread();
+
+ int nestedLevel = aMsg.nested_level();
+
+ MOZ_RELEASE_ASSERT(nestedLevel == IPC::Message::NOT_NESTED || NS_IsMainThread());
+
+ MessageChannel* dummy;
+ MessageChannel*& blockingVar = mSide == ChildSide && NS_IsMainThread() ? gParentProcessBlocker : dummy;
+
+ Result rv;
+ {
+ AutoSetValue<MessageChannel*> blocked(blockingVar, this);
+ rv = mListener->OnMessageReceived(aMsg, aReply);
+ }
+
+ if (!MaybeHandleError(rv, aMsg, "DispatchSyncMessage")) {
+ aReply = new Message();
+ aReply->set_sync();
+ aReply->set_nested_level(aMsg.nested_level());
+ aReply->set_reply();
+ aReply->set_reply_error();
+ }
+ aReply->set_seqno(aMsg.seqno());
+ aReply->set_transaction_id(aMsg.transaction_id());
+}
+
+void
+MessageChannel::DispatchAsyncMessage(const Message& aMsg)
+{
+ AssertWorkerThread();
+ MOZ_RELEASE_ASSERT(!aMsg.is_interrupt() && !aMsg.is_sync());
+
+ if (aMsg.routing_id() == MSG_ROUTING_NONE) {
+ NS_RUNTIMEABORT("unhandled special message!");
+ }
+
+ Result rv;
+ {
+ int nestedLevel = aMsg.nested_level();
+ AutoSetValue<bool> async(mDispatchingAsyncMessage, true);
+ AutoSetValue<int> nestedLevelSet(mDispatchingAsyncMessageNestedLevel, nestedLevel);
+ rv = mListener->OnMessageReceived(aMsg);
+ }
+ MaybeHandleError(rv, aMsg, "DispatchAsyncMessage");
+}
+
+bool
+MessageChannel::ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDepth)
+{
+ AssertWorkerThread();
+
+ // We may or may not own the lock in this function, so don't access any
+ // channel state.
+
+ IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
+
+ // Race detection: see the long comment near mRemoteStackDepthGuess in
+ // MessageChannel.h. "Remote" stack depth means our side, and "local" means
+ // the other side.
+ if (aMsg.interrupt_remote_stack_depth_guess() == RemoteViewOfStackDepth(aStackDepth)) {
+ return false;
+ }
+
+ // Interrupt in-calls have raced. The winner, if there is one, gets to defer
+ // processing of the other side's in-call.
+ bool defer;
+ const MessageInfo parentMsgInfo =
+ (mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top();
+ const MessageInfo childMsgInfo =
+ (mSide == ChildSide) ? mInterruptStack.top() : MessageInfo(aMsg);
+ switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo))
+ {
+ case RIPChildWins:
+ defer = (mSide == ChildSide);
+ break;
+ case RIPParentWins:
+ defer = (mSide != ChildSide);
+ break;
+ case RIPError:
+ MOZ_CRASH("NYI: 'Error' Interrupt race policy");
+ default:
+ MOZ_CRASH("not reached");
+ }
+
+ return defer;
+}
+
+void
+MessageChannel::DispatchInterruptMessage(Message&& aMsg, size_t stackDepth)
+{
+ AssertWorkerThread();
+ mMonitor->AssertNotCurrentThreadOwns();
+
+ IPC_ASSERT(aMsg.is_interrupt() && !aMsg.is_reply(), "wrong message type");
+
+ if (ShouldDeferInterruptMessage(aMsg, stackDepth)) {
+ // We now know the other side's stack has one more frame
+ // than we thought.
+ ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
+ mDeferred.push(Move(aMsg));
+ return;
+ }
+
+ // If we "lost" a race and need to process the other side's in-call, we
+ // don't need to fix up the mRemoteStackDepthGuess here, because we're just
+ // about to increment it, which will make it correct again.
+
+#ifdef OS_WIN
+ SyncStackFrame frame(this, true);
+#endif
+
+ nsAutoPtr<Message> reply;
+
+ ++mRemoteStackDepthGuess;
+ Result rv = mListener->OnCallReceived(aMsg, *getter_Transfers(reply));
+ --mRemoteStackDepthGuess;
+
+ if (!MaybeHandleError(rv, aMsg, "DispatchInterruptMessage")) {
+ reply = new Message();
+ reply->set_interrupt();
+ reply->set_reply();
+ reply->set_reply_error();
+ }
+ reply->set_seqno(aMsg.seqno());
+
+ MonitorAutoLock lock(*mMonitor);
+ if (ChannelConnected == mChannelState) {
+ mLink->SendMessage(reply.forget());
+ }
+}
+
+void
+MessageChannel::MaybeUndeferIncall()
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ if (mDeferred.empty())
+ return;
+
+ size_t stackDepth = InterruptStackDepth();
+
+ Message& deferred = mDeferred.top();
+
+ // the other side can only *under*-estimate our actual stack depth
+ IPC_ASSERT(deferred.interrupt_remote_stack_depth_guess() <= stackDepth,
+ "fatal logic error");
+
+ if (ShouldDeferInterruptMessage(deferred, stackDepth)) {
+ return;
+ }
+
+ // maybe time to process this message
+ Message call(Move(deferred));
+ mDeferred.pop();
+
+ // fix up fudge factor we added to account for race
+ IPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
+ --mRemoteStackDepthGuess;
+
+ MOZ_RELEASE_ASSERT(call.nested_level() == IPC::Message::NOT_NESTED);
+ RefPtr<MessageTask> task = new MessageTask(this, Move(call));
+ mPending.insertBack(task);
+ task->Post();
+}
+
+void
+MessageChannel::EnteredCxxStack()
+{
+ mListener->EnteredCxxStack();
+}
+
+void
+MessageChannel::ExitedCxxStack()
+{
+ mListener->ExitedCxxStack();
+ if (mSawInterruptOutMsg) {
+ MonitorAutoLock lock(*mMonitor);
+ // see long comment in OnMaybeDequeueOne()
+ EnqueuePendingMessages();
+ mSawInterruptOutMsg = false;
+ }
+}
+
+void
+MessageChannel::EnteredCall()
+{
+ mListener->EnteredCall();
+}
+
+void
+MessageChannel::ExitedCall()
+{
+ mListener->ExitedCall();
+}
+
+void
+MessageChannel::EnteredSyncSend()
+{
+ mListener->OnEnteredSyncSend();
+}
+
+void
+MessageChannel::ExitedSyncSend()
+{
+ mListener->OnExitedSyncSend();
+}
+
+void
+MessageChannel::EnqueuePendingMessages()
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ MaybeUndeferIncall();
+
+ // XXX performance tuning knob: could process all or k pending
+ // messages here, rather than enqueuing for later processing
+
+ RepostAllMessages();
+}
+
+static inline bool
+IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
+{
+ return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
+ (aTimeout <= (PR_IntervalNow() - aStart));
+}
+
+bool
+MessageChannel::WaitResponse(bool aWaitTimedOut)
+{
+ if (aWaitTimedOut) {
+ if (mInTimeoutSecondHalf) {
+ // We've really timed out this time.
+ return false;
+ }
+ // Try a second time.
+ mInTimeoutSecondHalf = true;
+ } else {
+ mInTimeoutSecondHalf = false;
+ }
+ return true;
+}
+
+#ifndef OS_WIN
+bool
+MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */)
+{
+#ifdef DEBUG
+ // WARNING: We don't release the lock here. We can't because the link thread
+ // could signal at this time and we would miss it. Instead we require
+ // ArtificialTimeout() to be extremely simple.
+ if (mListener->ArtificialTimeout()) {
+ return false;
+ }
+#endif
+
+ PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
+ PR_INTERVAL_NO_TIMEOUT :
+ PR_MillisecondsToInterval(mTimeoutMs);
+ // XXX could optimize away this syscall for "no timeout" case if desired
+ PRIntervalTime waitStart = PR_IntervalNow();
+
+ mMonitor->Wait(timeout);
+
+ // If the timeout didn't expire, we know we received an event. The
+ // converse is not true.
+ return WaitResponse(IsTimeoutExpired(waitStart, timeout));
+}
+
+bool
+MessageChannel::WaitForInterruptNotify()
+{
+ return WaitForSyncNotify(true);
+}
+
+void
+MessageChannel::NotifyWorkerThread()
+{
+ mMonitor->Notify();
+}
+#endif
+
+bool
+MessageChannel::ShouldContinueFromTimeout()
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ bool cont;
+ {
+ MonitorAutoUnlock unlock(*mMonitor);
+ cont = mListener->ShouldContinueFromReplyTimeout();
+ mListener->ArtificialSleep();
+ }
+
+ static enum { UNKNOWN, NOT_DEBUGGING, DEBUGGING } sDebuggingChildren = UNKNOWN;
+
+ if (sDebuggingChildren == UNKNOWN) {
+ sDebuggingChildren = getenv("MOZ_DEBUG_CHILD_PROCESS") ? DEBUGGING : NOT_DEBUGGING;
+ }
+ if (sDebuggingChildren == DEBUGGING) {
+ return true;
+ }
+
+ return cont;
+}
+
+void
+MessageChannel::SetReplyTimeoutMs(int32_t aTimeoutMs)
+{
+ // Set channel timeout value. Since this is broken up into
+ // two period, the minimum timeout value is 2ms.
+ AssertWorkerThread();
+ mTimeoutMs = (aTimeoutMs <= 0)
+ ? kNoTimeout
+ : (int32_t)ceil((double)aTimeoutMs / 2.0);
+}
+
+void
+MessageChannel::OnChannelConnected(int32_t peer_id)
+{
+ MOZ_RELEASE_ASSERT(!mPeerPidSet);
+ mPeerPidSet = true;
+ mPeerPid = peer_id;
+ RefPtr<CancelableRunnable> task = mOnChannelConnectedTask;
+ mWorkerLoop->PostTask(task.forget());
+}
+
+void
+MessageChannel::DispatchOnChannelConnected()
+{
+ AssertWorkerThread();
+ MOZ_RELEASE_ASSERT(mPeerPidSet);
+ mListener->OnChannelConnected(mPeerPid);
+}
+
+void
+MessageChannel::ReportMessageRouteError(const char* channelName) const
+{
+ PrintErrorMessage(mSide, channelName, "Need a route");
+ mListener->ProcessingError(MsgRouteError, "MsgRouteError");
+}
+
+void
+MessageChannel::ReportConnectionError(const char* aChannelName, Message* aMsg) const
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ const char* errorMsg = nullptr;
+ switch (mChannelState) {
+ case ChannelClosed:
+ errorMsg = "Closed channel: cannot send/recv";
+ break;
+ case ChannelOpening:
+ errorMsg = "Opening channel: not yet ready for send/recv";
+ break;
+ case ChannelTimeout:
+ errorMsg = "Channel timeout: cannot send/recv";
+ break;
+ case ChannelClosing:
+ errorMsg = "Channel closing: too late to send/recv, messages will be lost";
+ break;
+ case ChannelError:
+ errorMsg = "Channel error: cannot send/recv";
+ break;
+
+ default:
+ NS_RUNTIMEABORT("unreached");
+ }
+
+ if (aMsg) {
+ char reason[512];
+ SprintfLiteral(reason,"(msgtype=0x%X,name=%s) %s",
+ aMsg->type(), aMsg->name(), errorMsg);
+
+ PrintErrorMessage(mSide, aChannelName, reason);
+ } else {
+ PrintErrorMessage(mSide, aChannelName, errorMsg);
+ }
+
+ MonitorAutoUnlock unlock(*mMonitor);
+ mListener->ProcessingError(MsgDropped, errorMsg);
+}
+
+bool
+MessageChannel::MaybeHandleError(Result code, const Message& aMsg, const char* channelName)
+{
+ if (MsgProcessed == code)
+ return true;
+
+ const char* errorMsg = nullptr;
+ switch (code) {
+ case MsgNotKnown:
+ errorMsg = "Unknown message: not processed";
+ break;
+ case MsgNotAllowed:
+ errorMsg = "Message not allowed: cannot be sent/recvd in this state";
+ break;
+ case MsgPayloadError:
+ errorMsg = "Payload error: message could not be deserialized";
+ break;
+ case MsgProcessingError:
+ errorMsg = "Processing error: message was deserialized, but the handler returned false (indicating failure)";
+ break;
+ case MsgRouteError:
+ errorMsg = "Route error: message sent to unknown actor ID";
+ break;
+ case MsgValueError:
+ errorMsg = "Value error: message was deserialized, but contained an illegal value";
+ break;
+
+ default:
+ NS_RUNTIMEABORT("unknown Result code");
+ return false;
+ }
+
+ char reason[512];
+ const char* msgname = StringFromIPCMessageType(aMsg.type());
+ if (msgname[0] == '?') {
+ SprintfLiteral(reason,"(msgtype=0x%X) %s", aMsg.type(), errorMsg);
+ } else {
+ SprintfLiteral(reason,"%s %s", msgname, errorMsg);
+ }
+
+ PrintErrorMessage(mSide, channelName, reason);
+
+ mListener->ProcessingError(code, reason);
+
+ return false;
+}
+
+void
+MessageChannel::OnChannelErrorFromLink()
+{
+ AssertLinkThread();
+ mMonitor->AssertCurrentThreadOwns();
+
+ IPC_LOG("OnChannelErrorFromLink");
+
+ if (InterruptStackDepth() > 0)
+ NotifyWorkerThread();
+
+ if (AwaitingSyncReply() || AwaitingIncomingMessage())
+ NotifyWorkerThread();
+
+ if (ChannelClosing != mChannelState) {
+ if (mAbortOnError) {
+ NS_RUNTIMEABORT("Aborting on channel error.");
+ }
+ mChannelState = ChannelError;
+ mMonitor->Notify();
+ }
+
+ PostErrorNotifyTask();
+}
+
+void
+MessageChannel::NotifyMaybeChannelError()
+{
+ mMonitor->AssertNotCurrentThreadOwns();
+
+ // TODO sort out Close() on this side racing with Close() on the other side
+ if (ChannelClosing == mChannelState) {
+ // the channel closed, but we received a "Goodbye" message warning us
+ // about it. no worries
+ mChannelState = ChannelClosed;
+ NotifyChannelClosed();
+ return;
+ }
+
+ Clear();
+
+ // Oops, error! Let the listener know about it.
+ mChannelState = ChannelError;
+
+ // IPDL assumes these notifications do not fire twice, so we do not let
+ // that happen.
+ if (mNotifiedChannelDone) {
+ return;
+ }
+ mNotifiedChannelDone = true;
+
+ // After this, the channel may be deleted. Based on the premise that
+ // mListener owns this channel, any calls back to this class that may
+ // work with mListener should still work on living objects.
+ mListener->OnChannelError();
+}
+
+void
+MessageChannel::OnNotifyMaybeChannelError()
+{
+ AssertWorkerThread();
+ mMonitor->AssertNotCurrentThreadOwns();
+
+ mChannelErrorTask = nullptr;
+
+ // OnChannelError holds mMonitor when it posts this task and this
+ // task cannot be allowed to run until OnChannelError has
+ // exited. We enforce that order by grabbing the mutex here which
+ // should only continue once OnChannelError has completed.
+ {
+ MonitorAutoLock lock(*mMonitor);
+ // nothing to do here
+ }
+
+ if (IsOnCxxStack()) {
+ mChannelErrorTask =
+ NewNonOwningCancelableRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError);
+ RefPtr<Runnable> task = mChannelErrorTask;
+ // 10 ms delay is completely arbitrary
+ mWorkerLoop->PostDelayedTask(task.forget(), 10);
+ return;
+ }
+
+ NotifyMaybeChannelError();
+}
+
+void
+MessageChannel::PostErrorNotifyTask()
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ if (mChannelErrorTask)
+ return;
+
+ // This must be the last code that runs on this thread!
+ mChannelErrorTask =
+ NewNonOwningCancelableRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError);
+ RefPtr<Runnable> task = mChannelErrorTask;
+ mWorkerLoop->PostTask(task.forget());
+}
+
+// Special async message.
+class GoodbyeMessage : public IPC::Message
+{
+public:
+ GoodbyeMessage() :
+ IPC::Message(MSG_ROUTING_NONE, GOODBYE_MESSAGE_TYPE)
+ {
+ }
+ static bool Read(const Message* msg) {
+ return true;
+ }
+ void Log(const std::string& aPrefix, FILE* aOutf) const {
+ fputs("(special `Goodbye' message)", aOutf);
+ }
+};
+
+void
+MessageChannel::SynchronouslyClose()
+{
+ AssertWorkerThread();
+ mMonitor->AssertCurrentThreadOwns();
+ mLink->SendClose();
+ while (ChannelClosed != mChannelState)
+ mMonitor->Wait();
+}
+
+void
+MessageChannel::CloseWithError()
+{
+ AssertWorkerThread();
+
+ MonitorAutoLock lock(*mMonitor);
+ if (ChannelConnected != mChannelState) {
+ return;
+ }
+ SynchronouslyClose();
+ mChannelState = ChannelError;
+ PostErrorNotifyTask();
+}
+
+void
+MessageChannel::CloseWithTimeout()
+{
+ AssertWorkerThread();
+
+ MonitorAutoLock lock(*mMonitor);
+ if (ChannelConnected != mChannelState) {
+ return;
+ }
+ SynchronouslyClose();
+ mChannelState = ChannelTimeout;
+}
+
+void
+MessageChannel::Close()
+{
+ AssertWorkerThread();
+
+ {
+ MonitorAutoLock lock(*mMonitor);
+
+ if (ChannelError == mChannelState || ChannelTimeout == mChannelState) {
+ // See bug 538586: if the listener gets deleted while the
+ // IO thread's NotifyChannelError event is still enqueued
+ // and subsequently deletes us, then the error event will
+ // also be deleted and the listener will never be notified
+ // of the channel error.
+ if (mListener) {
+ MonitorAutoUnlock unlock(*mMonitor);
+ NotifyMaybeChannelError();
+ }
+ return;
+ }
+
+ if (ChannelOpening == mChannelState) {
+ // SynchronouslyClose() waits for an ack from the other side, so
+ // the opening sequence should complete before this returns.
+ SynchronouslyClose();
+ mChannelState = ChannelError;
+ NotifyMaybeChannelError();
+ return;
+ }
+
+ if (ChannelClosed == mChannelState) {
+ // XXX be strict about this until there's a compelling reason
+ // to relax
+ NS_RUNTIMEABORT("Close() called on closed channel!");
+ }
+
+ // Notify the other side that we're about to close our socket. If we've
+ // already received a Goodbye from the other side (and our state is
+ // ChannelClosing), there's no reason to send one.
+ if (ChannelConnected == mChannelState) {
+ mLink->SendMessage(new GoodbyeMessage());
+ }
+ SynchronouslyClose();
+ }
+
+ NotifyChannelClosed();
+}
+
+void
+MessageChannel::NotifyChannelClosed()
+{
+ mMonitor->AssertNotCurrentThreadOwns();
+
+ if (ChannelClosed != mChannelState)
+ NS_RUNTIMEABORT("channel should have been closed!");
+
+ Clear();
+
+ // IPDL assumes these notifications do not fire twice, so we do not let
+ // that happen.
+ if (mNotifiedChannelDone) {
+ return;
+ }
+ mNotifiedChannelDone = true;
+
+ // OK, the IO thread just closed the channel normally. Let the
+ // listener know about it. After this point the channel may be
+ // deleted.
+ mListener->OnChannelClose();
+}
+
+void
+MessageChannel::DebugAbort(const char* file, int line, const char* cond,
+ const char* why,
+ bool reply)
+{
+ printf_stderr("###!!! [MessageChannel][%s][%s:%d] "
+ "Assertion (%s) failed. %s %s\n",
+ mSide == ChildSide ? "Child" : "Parent",
+ file, line, cond,
+ why,
+ reply ? "(reply)" : "");
+ // technically we need the mutex for this, but we're dying anyway
+ DumpInterruptStack(" ");
+ printf_stderr(" remote Interrupt stack guess: %" PRIuSIZE "\n",
+ mRemoteStackDepthGuess);
+ printf_stderr(" deferred stack size: %" PRIuSIZE "\n",
+ mDeferred.size());
+ printf_stderr(" out-of-turn Interrupt replies stack size: %" PRIuSIZE "\n",
+ mOutOfTurnReplies.size());
+
+ MessageQueue pending = Move(mPending);
+ while (!pending.isEmpty()) {
+ printf_stderr(" [ %s%s ]\n",
+ pending.getFirst()->Msg().is_interrupt() ? "intr" :
+ (pending.getFirst()->Msg().is_sync() ? "sync" : "async"),
+ pending.getFirst()->Msg().is_reply() ? "reply" : "");
+ pending.popFirst();
+ }
+
+ NS_RUNTIMEABORT(why);
+}
+
+void
+MessageChannel::DumpInterruptStack(const char* const pfx) const
+{
+ NS_WARNING_ASSERTION(
+ MessageLoop::current() != mWorkerLoop,
+ "The worker thread had better be paused in a debugger!");
+
+ printf_stderr("%sMessageChannel 'backtrace':\n", pfx);
+
+ // print a python-style backtrace, first frame to last
+ for (uint32_t i = 0; i < mCxxStackFrames.length(); ++i) {
+ int32_t id;
+ const char* dir;
+ const char* sems;
+ const char* name;
+ mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
+
+ printf_stderr("%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
+ i, dir, sems, name, id);
+ }
+}
+
+int32_t
+MessageChannel::GetTopmostMessageRoutingId() const
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == mWorkerLoop);
+ if (mCxxStackFrames.empty()) {
+ return MSG_ROUTING_NONE;
+ }
+ const InterruptFrame& frame = mCxxStackFrames.back();
+ return frame.GetRoutingId();
+}
+
+void
+MessageChannel::EndTimeout()
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ IPC_LOG("Ending timeout of seqno=%d", mTimedOutMessageSeqno);
+ mTimedOutMessageSeqno = 0;
+ mTimedOutMessageNestedLevel = 0;
+
+ RepostAllMessages();
+}
+
+void
+MessageChannel::RepostAllMessages()
+{
+ bool needRepost = false;
+ for (RefPtr<MessageTask> task : mPending) {
+ if (!task->IsScheduled()) {
+ needRepost = true;
+ }
+ }
+ if (!needRepost) {
+ // If everything is already scheduled to run, do nothing.
+ return;
+ }
+
+ // In some cases we may have deferred dispatch of some messages in the
+ // queue. Now we want to run them again. However, we can't just re-post
+ // those messages since the messages after them in mPending would then be
+ // before them in the event queue. So instead we cancel everything and
+ // re-post all messages in the correct order.
+ MessageQueue queue = Move(mPending);
+ while (RefPtr<MessageTask> task = queue.popFirst()) {
+ RefPtr<MessageTask> newTask = new MessageTask(this, Move(task->Msg()));
+ mPending.insertBack(newTask);
+ newTask->Post();
+ }
+}
+
+void
+MessageChannel::CancelTransaction(int transaction)
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ // When we cancel a transaction, we need to behave as if there's no longer
+ // any IPC on the stack. Anything we were dispatching or sending will get
+ // canceled. Consequently, we have to update the state variables below.
+ //
+ // We also need to ensure that when any IPC functions on the stack return,
+ // they don't reset these values using an RAII class like AutoSetValue. To
+ // avoid that, these RAII classes check if the variable they set has been
+ // tampered with (by us). If so, they don't reset the variable to the old
+ // value.
+
+ IPC_LOG("CancelTransaction: xid=%d", transaction);
+
+ // An unusual case: We timed out a transaction which the other side then
+ // cancelled. In this case we just leave the timedout state and try to
+ // forget this ever happened.
+ if (transaction == mTimedOutMessageSeqno) {
+ IPC_LOG("Cancelled timed out message %d", mTimedOutMessageSeqno);
+ EndTimeout();
+
+ // Normally mCurrentTransaction == 0 here. But it can be non-zero if:
+ // 1. Parent sends NESTED_INSIDE_SYNC message H.
+ // 2. Parent times out H.
+ // 3. Child dispatches H and sends nested message H' (same transaction).
+ // 4. Parent dispatches H' and cancels.
+ MOZ_RELEASE_ASSERT(!mTransactionStack || mTransactionStack->TransactionID() == transaction);
+ if (mTransactionStack) {
+ mTransactionStack->Cancel();
+ }
+ } else {
+ MOZ_RELEASE_ASSERT(mTransactionStack->TransactionID() == transaction);
+ mTransactionStack->Cancel();
+ }
+
+ bool foundSync = false;
+ for (RefPtr<MessageTask> p = mPending.getFirst(); p; ) {
+ Message &msg = p->Msg();
+
+ // If there was a race between the parent and the child, then we may
+ // have a queued sync message. We want to drop this message from the
+ // queue since if will get cancelled along with the transaction being
+ // cancelled. This happens if the message in the queue is NESTED_INSIDE_SYNC.
+ if (msg.is_sync() && msg.nested_level() != IPC::Message::NOT_NESTED) {
+ MOZ_RELEASE_ASSERT(!foundSync);
+ MOZ_RELEASE_ASSERT(msg.transaction_id() != transaction);
+ IPC_LOG("Removing msg from queue seqno=%d xid=%d", msg.seqno(), msg.transaction_id());
+ foundSync = true;
+ p = p->removeAndGetNext();
+ continue;
+ }
+
+ p = p->getNext();
+ }
+}
+
+bool
+MessageChannel::IsInTransaction() const
+{
+ MonitorAutoLock lock(*mMonitor);
+ return !!mTransactionStack;
+}
+
+void
+MessageChannel::CancelCurrentTransaction()
+{
+ MonitorAutoLock lock(*mMonitor);
+ if (DispatchingSyncMessageNestedLevel() >= IPC::Message::NESTED_INSIDE_SYNC) {
+ if (DispatchingSyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW ||
+ DispatchingAsyncMessageNestedLevel() == IPC::Message::NESTED_INSIDE_CPOW)
+ {
+ mListener->IntentionalCrash();
+ }
+
+ IPC_LOG("Cancel requested: current xid=%d", CurrentNestedInsideSyncTransaction());
+ MOZ_RELEASE_ASSERT(DispatchingSyncMessage());
+ CancelMessage *cancel = new CancelMessage(CurrentNestedInsideSyncTransaction());
+ CancelTransaction(CurrentNestedInsideSyncTransaction());
+ mLink->SendMessage(cancel);
+ }
+}
+
+void
+CancelCPOWs()
+{
+ if (gParentProcessBlocker) {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_TRANSACTION_CANCEL, true);
+ gParentProcessBlocker->CancelCurrentTransaction();
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h
new file mode 100644
index 000000000..df70899df
--- /dev/null
+++ b/ipc/glue/MessageChannel.h
@@ -0,0 +1,718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 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/. */
+
+#ifndef ipc_glue_MessageChannel_h
+#define ipc_glue_MessageChannel_h 1
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+
+#include "mozilla/Function.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Vector.h"
+#if defined(OS_WIN)
+#include "mozilla/ipc/Neutering.h"
+#endif // defined(OS_WIN)
+#include "mozilla/ipc/Transport.h"
+#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
+#include "mozilla/mozalloc_oom.h"
+#include "nsExceptionHandler.h"
+#endif
+#include "MessageLink.h"
+
+#include <deque>
+#include <stack>
+#include <math.h>
+
+namespace mozilla {
+namespace ipc {
+
+class MessageChannel;
+class IToplevelProtocol;
+
+class RefCountedMonitor : public Monitor
+{
+ public:
+ RefCountedMonitor()
+ : Monitor("mozilla.ipc.MessageChannel.mMonitor")
+ {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMonitor)
+
+ private:
+ ~RefCountedMonitor() {}
+};
+
+enum class SyncSendError {
+ SendSuccess,
+ PreviousTimeout,
+ SendingCPOWWhileDispatchingSync,
+ SendingCPOWWhileDispatchingUrgent,
+ NotConnectedBeforeSend,
+ DisconnectedDuringSend,
+ CancelledBeforeSend,
+ CancelledAfterSend,
+ TimedOut,
+ ReplyError,
+};
+
+enum ChannelState {
+ ChannelClosed,
+ ChannelOpening,
+ ChannelConnected,
+ ChannelTimeout,
+ ChannelClosing,
+ ChannelError
+};
+
+class AutoEnterTransaction;
+
+class MessageChannel : HasResultCodes
+{
+ friend class ProcessLink;
+ friend class ThreadLink;
+
+ class CxxStackFrame;
+ class InterruptFrame;
+
+ typedef mozilla::Monitor Monitor;
+
+ public:
+ static const int32_t kNoTimeout;
+
+ typedef IPC::Message Message;
+ typedef IPC::MessageInfo MessageInfo;
+ typedef mozilla::ipc::Transport Transport;
+
+ explicit MessageChannel(IToplevelProtocol *aListener);
+ ~MessageChannel();
+
+ // "Open" from the perspective of the transport layer; the underlying
+ // socketpair/pipe should already be created.
+ //
+ // Returns true if the transport layer was successfully connected,
+ // i.e., mChannelState == ChannelConnected.
+ bool Open(Transport* aTransport, MessageLoop* aIOLoop=0, Side aSide=UnknownSide);
+
+ // "Open" a connection to another thread in the same process.
+ //
+ // Returns true if the transport layer was successfully connected,
+ // i.e., mChannelState == ChannelConnected.
+ //
+ // For more details on the process of opening a channel between
+ // threads, see the extended comment on this function
+ // in MessageChannel.cpp.
+ bool Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side aSide);
+
+ // Close the underlying transport channel.
+ void Close();
+
+ // Force the channel to behave as if a channel error occurred. Valid
+ // for process links only, not thread links.
+ void CloseWithError();
+
+ void CloseWithTimeout();
+
+ void SetAbortOnError(bool abort)
+ {
+ mAbortOnError = abort;
+ }
+
+ // Call aInvoke for each pending message until it returns false.
+ // XXX: You must get permission from an IPC peer to use this function
+ // since it requires custom deserialization and re-orders events.
+ void PeekMessages(mozilla::function<bool(const Message& aMsg)> aInvoke);
+
+ // Misc. behavioral traits consumers can request for this channel
+ enum ChannelFlags {
+ REQUIRE_DEFAULT = 0,
+ // Windows: if this channel operates on the UI thread, indicates
+ // WindowsMessageLoop code should enable deferred native message
+ // handling to prevent deadlocks. Should only be used for protocols
+ // that manage child processes which might create native UI, like
+ // plugins.
+ REQUIRE_DEFERRED_MESSAGE_PROTECTION = 1 << 0,
+ // Windows: When this flag is specified, any wait that occurs during
+ // synchronous IPC will be alertable, thus allowing a11y code in the
+ // chrome process to reenter content while content is waiting on a
+ // synchronous call.
+ REQUIRE_A11Y_REENTRY = 1 << 1,
+ };
+ void SetChannelFlags(ChannelFlags aFlags) { mFlags = aFlags; }
+ ChannelFlags GetChannelFlags() { return mFlags; }
+
+ // Asynchronously send a message to the other side of the channel
+ bool Send(Message* aMsg);
+
+ // Asynchronously deliver a message back to this side of the
+ // channel
+ bool Echo(Message* aMsg);
+
+ // Synchronously send |msg| (i.e., wait for |reply|)
+ bool Send(Message* aMsg, Message* aReply);
+
+ // Make an Interrupt call to the other side of the channel
+ bool Call(Message* aMsg, Message* aReply);
+
+ // Wait until a message is received
+ bool WaitForIncomingMessage();
+
+ bool CanSend() const;
+
+ // If sending a sync message returns an error, this function gives a more
+ // descriptive error message.
+ SyncSendError LastSendError() const {
+ AssertWorkerThread();
+ return mLastSendError;
+ }
+
+ // Currently only for debugging purposes, doesn't aquire mMonitor.
+ ChannelState GetChannelState__TotallyRacy() const {
+ return mChannelState;
+ }
+
+ void SetReplyTimeoutMs(int32_t aTimeoutMs);
+
+ bool IsOnCxxStack() const {
+ return !mCxxStackFrames.empty();
+ }
+
+ bool IsInTransaction() const;
+ void CancelCurrentTransaction();
+
+ /**
+ * This function is used by hang annotation code to determine which IPDL
+ * actor is highest in the call stack at the time of the hang. It should
+ * be called from the main thread when a sync or intr message is about to
+ * be sent.
+ */
+ int32_t GetTopmostMessageRoutingId() const;
+
+ // Unsound_IsClosed and Unsound_NumQueuedMessages are safe to call from any
+ // thread, but they make no guarantees about whether you'll get an
+ // up-to-date value; the values are written on one thread and read without
+ // locking, on potentially different threads. Thus you should only use
+ // them when you don't particularly care about getting a recent value (e.g.
+ // in a memory report).
+ bool Unsound_IsClosed() const {
+ return mLink ? mLink->Unsound_IsClosed() : true;
+ }
+ uint32_t Unsound_NumQueuedMessages() const {
+ return mLink ? mLink->Unsound_NumQueuedMessages() : 0;
+ }
+
+ static bool IsPumpingMessages() {
+ return sIsPumpingMessages;
+ }
+ static void SetIsPumpingMessages(bool aIsPumping) {
+ sIsPumpingMessages = aIsPumping;
+ }
+
+#ifdef OS_WIN
+ struct MOZ_STACK_CLASS SyncStackFrame
+ {
+ SyncStackFrame(MessageChannel* channel, bool interrupt);
+ ~SyncStackFrame();
+
+ bool mInterrupt;
+ bool mSpinNestedEvents;
+ bool mListenerNotified;
+ MessageChannel* mChannel;
+
+ // The previous stack frame for this channel.
+ SyncStackFrame* mPrev;
+
+ // The previous stack frame on any channel.
+ SyncStackFrame* mStaticPrev;
+ };
+ friend struct MessageChannel::SyncStackFrame;
+
+ static bool IsSpinLoopActive() {
+ for (SyncStackFrame* frame = sStaticTopFrame; frame; frame = frame->mPrev) {
+ if (frame->mSpinNestedEvents)
+ return true;
+ }
+ return false;
+ }
+
+ protected:
+ // The deepest sync stack frame for this channel.
+ SyncStackFrame* mTopFrame;
+
+ bool mIsSyncWaitingOnNonMainThread;
+
+ // The deepest sync stack frame on any channel.
+ static SyncStackFrame* sStaticTopFrame;
+
+ public:
+ void ProcessNativeEventsInInterruptCall();
+ static void NotifyGeckoEventDispatch();
+
+ private:
+ void SpinInternalEventLoop();
+#if defined(ACCESSIBILITY)
+ bool WaitForSyncNotifyWithA11yReentry();
+#endif // defined(ACCESSIBILITY)
+#endif // defined(OS_WIN)
+
+ private:
+ void CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide);
+ void OnOpenAsSlave(MessageChannel *aTargetChan, Side aSide);
+
+ void PostErrorNotifyTask();
+ void OnNotifyMaybeChannelError();
+ void ReportConnectionError(const char* aChannelName, Message* aMsg = nullptr) const;
+ void ReportMessageRouteError(const char* channelName) const;
+ bool MaybeHandleError(Result code, const Message& aMsg, const char* channelName);
+
+ void Clear();
+
+ // Send OnChannelConnected notification to listeners.
+ void DispatchOnChannelConnected();
+
+ bool InterruptEventOccurred();
+ bool HasPendingEvents();
+
+ void ProcessPendingRequests(AutoEnterTransaction& aTransaction);
+ bool ProcessPendingRequest(Message &&aUrgent);
+
+ void MaybeUndeferIncall();
+ void EnqueuePendingMessages();
+
+ // Dispatches an incoming message to its appropriate handler.
+ void DispatchMessage(Message &&aMsg);
+
+ // DispatchMessage will route to one of these functions depending on the
+ // protocol type of the message.
+ void DispatchSyncMessage(const Message &aMsg, Message*& aReply);
+ void DispatchUrgentMessage(const Message &aMsg);
+ void DispatchAsyncMessage(const Message &aMsg);
+ void DispatchRPCMessage(const Message &aMsg);
+ void DispatchInterruptMessage(Message &&aMsg, size_t aStackDepth);
+
+ // Return true if the wait ended because a notification was received.
+ //
+ // Return false if the time elapsed from when we started the process of
+ // waiting until afterwards exceeded the currently allotted timeout.
+ // That *DOES NOT* mean false => "no event" (== timeout); there are many
+ // circumstances that could cause the measured elapsed time to exceed the
+ // timeout EVEN WHEN we were notified.
+ //
+ // So in sum: true is a meaningful return value; false isn't,
+ // necessarily.
+ bool WaitForSyncNotify(bool aHandleWindowsMessages);
+ bool WaitForInterruptNotify();
+
+ bool WaitResponse(bool aWaitTimedOut);
+
+ bool ShouldContinueFromTimeout();
+
+ void EndTimeout();
+ void CancelTransaction(int transaction);
+
+ void RepostAllMessages();
+
+ // The "remote view of stack depth" can be different than the
+ // actual stack depth when there are out-of-turn replies. When we
+ // receive one, our actual Interrupt stack depth doesn't decrease, but
+ // the other side (that sent the reply) thinks it has. So, the
+ // "view" returned here is |stackDepth| minus the number of
+ // out-of-turn replies.
+ //
+ // Only called from the worker thread.
+ size_t RemoteViewOfStackDepth(size_t stackDepth) const {
+ AssertWorkerThread();
+ return stackDepth - mOutOfTurnReplies.size();
+ }
+
+ int32_t NextSeqno() {
+ AssertWorkerThread();
+ return (mSide == ChildSide) ? --mNextSeqno : ++mNextSeqno;
+ }
+
+ // This helper class manages mCxxStackDepth on behalf of MessageChannel.
+ // When the stack depth is incremented from zero to non-zero, it invokes
+ // a callback, and similarly for when the depth goes from non-zero to zero.
+ void EnteredCxxStack();
+ void ExitedCxxStack();
+
+ void EnteredCall();
+ void ExitedCall();
+
+ void EnteredSyncSend();
+ void ExitedSyncSend();
+
+ IToplevelProtocol *Listener() const {
+ return mListener;
+ }
+
+ void DebugAbort(const char* file, int line, const char* cond,
+ const char* why,
+ bool reply=false);
+
+ // This method is only safe to call on the worker thread, or in a
+ // debugger with all threads paused.
+ void DumpInterruptStack(const char* const pfx="") const;
+
+ private:
+ // Called from both threads
+ size_t InterruptStackDepth() const {
+ mMonitor->AssertCurrentThreadOwns();
+ return mInterruptStack.size();
+ }
+
+ bool AwaitingInterruptReply() const {
+ mMonitor->AssertCurrentThreadOwns();
+ return !mInterruptStack.empty();
+ }
+ bool AwaitingIncomingMessage() const {
+ mMonitor->AssertCurrentThreadOwns();
+ return mIsWaitingForIncoming;
+ }
+
+ class MOZ_STACK_CLASS AutoEnterWaitForIncoming
+ {
+ public:
+ explicit AutoEnterWaitForIncoming(MessageChannel& aChannel)
+ : mChannel(aChannel)
+ {
+ aChannel.mMonitor->AssertCurrentThreadOwns();
+ aChannel.mIsWaitingForIncoming = true;
+ }
+
+ ~AutoEnterWaitForIncoming()
+ {
+ mChannel.mIsWaitingForIncoming = false;
+ }
+
+ private:
+ MessageChannel& mChannel;
+ };
+ friend class AutoEnterWaitForIncoming;
+
+ // Returns true if we're dispatching an async message's callback.
+ bool DispatchingAsyncMessage() const {
+ AssertWorkerThread();
+ return mDispatchingAsyncMessage;
+ }
+
+ int DispatchingAsyncMessageNestedLevel() const {
+ AssertWorkerThread();
+ return mDispatchingAsyncMessageNestedLevel;
+ }
+
+ bool Connected() const;
+
+ private:
+ // Executed on the IO thread.
+ void NotifyWorkerThread();
+
+ // Return true if |aMsg| is a special message targeted at the IO
+ // thread, in which case it shouldn't be delivered to the worker.
+ bool MaybeInterceptSpecialIOMessage(const Message& aMsg);
+
+ void OnChannelConnected(int32_t peer_id);
+
+ // Tell the IO thread to close the channel and wait for it to ACK.
+ void SynchronouslyClose();
+
+ bool WasTransactionCanceled(int transaction);
+ bool ShouldDeferMessage(const Message& aMsg);
+ bool ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDepth);
+ void OnMessageReceivedFromLink(Message&& aMsg);
+ void OnChannelErrorFromLink();
+
+ private:
+ // Run on the not current thread.
+ void NotifyChannelClosed();
+ void NotifyMaybeChannelError();
+
+ private:
+ // Can be run on either thread
+ void AssertWorkerThread() const
+ {
+ MOZ_RELEASE_ASSERT(mWorkerLoopID == MessageLoop::current()->id(),
+ "not on worker thread!");
+ }
+
+ // The "link" thread is either the I/O thread (ProcessLink) or the
+ // other actor's work thread (ThreadLink). In either case, it is
+ // NOT our worker thread.
+ void AssertLinkThread() const
+ {
+ MOZ_RELEASE_ASSERT(mWorkerLoopID != MessageLoop::current()->id(),
+ "on worker thread but should not be!");
+ }
+
+ private:
+ class MessageTask :
+ public CancelableRunnable,
+ public LinkedListElement<RefPtr<MessageTask>>,
+ public nsIRunnablePriority
+ {
+ public:
+ explicit MessageTask(MessageChannel* aChannel, Message&& aMessage)
+ : mChannel(aChannel), mMessage(Move(aMessage)), mScheduled(false)
+ {}
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_IMETHOD Run() override;
+ nsresult Cancel() override;
+ NS_IMETHOD GetPriority(uint32_t* aPriority) override;
+ void Post();
+ void Clear();
+
+ bool IsScheduled() const { return mScheduled; }
+
+ Message& Msg() { return mMessage; }
+ const Message& Msg() const { return mMessage; }
+
+ private:
+ MessageTask() = delete;
+ MessageTask(const MessageTask&) = delete;
+ ~MessageTask() {}
+
+ MessageChannel* mChannel;
+ Message mMessage;
+ bool mScheduled : 1;
+ };
+
+ bool ShouldRunMessage(const Message& aMsg);
+ void RunMessage(MessageTask& aTask);
+
+ typedef LinkedList<RefPtr<MessageTask>> MessageQueue;
+ typedef std::map<size_t, Message> MessageMap;
+ typedef IPC::Message::msgid_t msgid_t;
+
+ private:
+ // Based on presumption the listener owns and overlives the channel,
+ // this is never nullified.
+ IToplevelProtocol* mListener;
+ ChannelState mChannelState;
+ RefPtr<RefCountedMonitor> mMonitor;
+ Side mSide;
+ MessageLink* mLink;
+ MessageLoop* mWorkerLoop; // thread where work is done
+ RefPtr<CancelableRunnable> mChannelErrorTask; // NotifyMaybeChannelError runnable
+
+ // id() of mWorkerLoop. This persists even after mWorkerLoop is cleared
+ // during channel shutdown.
+ int mWorkerLoopID;
+
+ // Timeout periods are broken up in two to prevent system suspension from
+ // triggering an abort. This method (called by WaitForEvent with a 'did
+ // timeout' flag) decides if we should wait again for half of mTimeoutMs
+ // or give up.
+ int32_t mTimeoutMs;
+ bool mInTimeoutSecondHalf;
+
+ // Worker-thread only; sequence numbers for messages that require
+ // synchronous replies.
+ int32_t mNextSeqno;
+
+ static bool sIsPumpingMessages;
+
+ // If ::Send returns false, this gives a more descriptive error.
+ SyncSendError mLastSendError;
+
+ template<class T>
+ class AutoSetValue {
+ public:
+ explicit AutoSetValue(T &var, const T &newValue)
+ : mVar(var), mPrev(var), mNew(newValue)
+ {
+ mVar = newValue;
+ }
+ ~AutoSetValue() {
+ // The value may have been zeroed if the transaction was
+ // canceled. In that case we shouldn't return it to its previous
+ // value.
+ if (mVar == mNew) {
+ mVar = mPrev;
+ }
+ }
+ private:
+ T& mVar;
+ T mPrev;
+ T mNew;
+ };
+
+ bool mDispatchingAsyncMessage;
+ int mDispatchingAsyncMessageNestedLevel;
+
+ // When we send an urgent request from the parent process, we could race
+ // with an RPC message that was issued by the child beforehand. In this
+ // case, if the parent were to wake up while waiting for the urgent reply,
+ // and process the RPC, it could send an additional urgent message. The
+ // child would wake up to process the urgent message (as it always will),
+ // then send a reply, which could be received by the parent out-of-order
+ // with respect to the first urgent reply.
+ //
+ // To address this problem, urgent or RPC requests are associated with a
+ // "transaction". Whenever one side of the channel wishes to start a
+ // chain of RPC/urgent messages, it allocates a new transaction ID. Any
+ // messages the parent receives, not apart of this transaction, are
+ // deferred. When issuing RPC/urgent requests on top of a started
+ // transaction, the initiating transaction ID is used.
+ //
+ // To ensure IDs are unique, we use sequence numbers for transaction IDs,
+ // which grow in opposite directions from child to parent.
+
+ friend class AutoEnterTransaction;
+ AutoEnterTransaction *mTransactionStack;
+
+ int32_t CurrentNestedInsideSyncTransaction() const;
+
+ bool AwaitingSyncReply() const;
+ int AwaitingSyncReplyNestedLevel() const;
+
+ bool DispatchingSyncMessage() const;
+ int DispatchingSyncMessageNestedLevel() const;
+
+ // If a sync message times out, we store its sequence number here. Any
+ // future sync messages will fail immediately. Once the reply for original
+ // sync message is received, we allow sync messages again.
+ //
+ // When a message times out, nothing is done to inform the other side. The
+ // other side will eventually dispatch the message and send a reply. Our
+ // side is responsible for replying to all sync messages sent by the other
+ // side when it dispatches the timed out message. The response is always an
+ // error.
+ //
+ // A message is only timed out if it initiated a transaction. This avoids
+ // hitting a lot of corner cases with message nesting that we don't really
+ // care about.
+ int32_t mTimedOutMessageSeqno;
+ int mTimedOutMessageNestedLevel;
+
+ // Queue of all incoming messages.
+ //
+ // If both this side and the other side are functioning correctly, the queue
+ // can only be in certain configurations. Let
+ //
+ // |A<| be an async in-message,
+ // |S<| be a sync in-message,
+ // |C<| be an Interrupt in-call,
+ // |R<| be an Interrupt reply.
+ //
+ // The queue can only match this configuration
+ //
+ // A<* (S< | C< | R< (?{mInterruptStack.size() == 1} A<* (S< | C<)))
+ //
+ // The other side can send as many async messages |A<*| as it wants before
+ // sending us a blocking message.
+ //
+ // The first case is |S<|, a sync in-msg. The other side must be blocked,
+ // and thus can't send us any more messages until we process the sync
+ // in-msg.
+ //
+ // The second case is |C<|, an Interrupt in-call; the other side must be blocked.
+ // (There's a subtlety here: this in-call might have raced with an
+ // out-call, but we detect that with the mechanism below,
+ // |mRemoteStackDepth|, and races don't matter to the queue.)
+ //
+ // Final case, the other side replied to our most recent out-call |R<|.
+ // If that was the *only* out-call on our stack, |?{mInterruptStack.size() == 1}|,
+ // then other side "finished with us," and went back to its own business.
+ // That business might have included sending any number of async message
+ // |A<*| until sending a blocking message |(S< | C<)|. If we had more than
+ // one Interrupt call on our stack, the other side *better* not have sent us
+ // another blocking message, because it's blocked on a reply from us.
+ //
+ MessageQueue mPending;
+
+ // Stack of all the out-calls on which this channel is awaiting responses.
+ // Each stack refers to a different protocol and the stacks are mutually
+ // exclusive: multiple outcalls of the same kind cannot be initiated while
+ // another is active.
+ std::stack<MessageInfo> mInterruptStack;
+
+ // This is what we think the Interrupt stack depth is on the "other side" of this
+ // Interrupt channel. We maintain this variable so that we can detect racy Interrupt
+ // calls. With each Interrupt out-call sent, we send along what *we* think the
+ // stack depth of the remote side is *before* it will receive the Interrupt call.
+ //
+ // After sending the out-call, our stack depth is "incremented" by pushing
+ // that pending message onto mPending.
+ //
+ // Then when processing an in-call |c|, it must be true that
+ //
+ // mInterruptStack.size() == c.remoteDepth
+ //
+ // I.e., my depth is actually the same as what the other side thought it
+ // was when it sent in-call |c|. If this fails to hold, we have detected
+ // racy Interrupt calls.
+ //
+ // We then increment mRemoteStackDepth *just before* processing the
+ // in-call, since we know the other side is waiting on it, and decrement
+ // it *just after* finishing processing that in-call, since our response
+ // will pop the top of the other side's |mPending|.
+ //
+ // One nice aspect of this race detection is that it is symmetric; if one
+ // side detects a race, then the other side must also detect the same race.
+ size_t mRemoteStackDepthGuess;
+
+ // Approximation of code frames on the C++ stack. It can only be
+ // interpreted as the implication:
+ //
+ // !mCxxStackFrames.empty() => MessageChannel code on C++ stack
+ //
+ // This member is only accessed on the worker thread, and so is not
+ // protected by mMonitor. It is managed exclusively by the helper
+ // |class CxxStackFrame|.
+ mozilla::Vector<InterruptFrame> mCxxStackFrames;
+
+ // Did we process an Interrupt out-call during this stack? Only meaningful in
+ // ExitedCxxStack(), from which this variable is reset.
+ bool mSawInterruptOutMsg;
+
+ // Are we waiting on this channel for an incoming message? This is used
+ // to implement WaitForIncomingMessage(). Must only be accessed while owning
+ // mMonitor.
+ bool mIsWaitingForIncoming;
+
+ // Map of replies received "out of turn", because of Interrupt
+ // in-calls racing with replies to outstanding in-calls. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
+ MessageMap mOutOfTurnReplies;
+
+ // Stack of Interrupt in-calls that were deferred because of race
+ // conditions.
+ std::stack<Message> mDeferred;
+
+#ifdef OS_WIN
+ HANDLE mEvent;
+#endif
+
+ // Should the channel abort the process from the I/O thread when
+ // a channel error occurs?
+ bool mAbortOnError;
+
+ // True if the listener has already been notified of a channel close or
+ // error.
+ bool mNotifiedChannelDone;
+
+ // See SetChannelFlags
+ ChannelFlags mFlags;
+
+ // Task and state used to asynchronously notify channel has been connected
+ // safely. This is necessary to be able to cancel notification if we are
+ // closed at the same time.
+ RefPtr<CancelableRunnable> mOnChannelConnectedTask;
+ bool mPeerPidSet;
+ int32_t mPeerPid;
+};
+
+void
+CancelCPOWs();
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // ifndef ipc_glue_MessageChannel_h
diff --git a/ipc/glue/MessageLink.cpp b/ipc/glue/MessageLink.cpp
new file mode 100644
index 000000000..6a1bda02d
--- /dev/null
+++ b/ipc/glue/MessageLink.cpp
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ipc/MessageLink.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "chrome/common/ipc_channel.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDebug.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+#include "nsISupportsImpl.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla;
+using namespace std;
+
+// We rely on invariants about the lifetime of the transport:
+//
+// - outlives this MessageChannel
+// - deleted on the IO thread
+//
+// These invariants allow us to send messages directly through the
+// transport without having to worry about orphaned Send() tasks on
+// the IO thread touching MessageChannel memory after it's been deleted
+// on the worker thread. We also don't need to refcount the
+// Transport, because whatever task triggers its deletion only runs on
+// the IO thread, and only runs after this MessageChannel is done with
+// the Transport.
+
+namespace mozilla {
+namespace ipc {
+
+MessageLink::MessageLink(MessageChannel *aChan)
+ : mChan(aChan)
+{
+}
+
+MessageLink::~MessageLink()
+{
+#ifdef DEBUG
+ mChan = nullptr;
+#endif
+}
+
+ProcessLink::ProcessLink(MessageChannel *aChan)
+ : MessageLink(aChan)
+ , mTransport(nullptr)
+ , mIOLoop(nullptr)
+ , mExistingListener(nullptr)
+{
+}
+
+ProcessLink::~ProcessLink()
+{
+#ifdef DEBUG
+ mTransport = nullptr;
+ mIOLoop = nullptr;
+ mExistingListener = nullptr;
+#endif
+}
+
+void
+ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide)
+{
+ NS_PRECONDITION(aTransport, "need transport layer");
+
+ // FIXME need to check for valid channel
+
+ mTransport = aTransport;
+
+ // FIXME figure out whether we're in parent or child, grab IO loop
+ // appropriately
+ bool needOpen = true;
+ if(aIOLoop) {
+ // We're a child or using the new arguments. Either way, we
+ // need an open.
+ needOpen = true;
+ mChan->mSide = (aSide == UnknownSide) ? ChildSide : aSide;
+ } else {
+ NS_PRECONDITION(aSide == UnknownSide, "expected default side arg");
+
+ // parent
+ mChan->mSide = ParentSide;
+ needOpen = false;
+ aIOLoop = XRE_GetIOMessageLoop();
+ }
+
+ mIOLoop = aIOLoop;
+
+ NS_ASSERTION(mIOLoop, "need an IO loop");
+ NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop");
+
+ {
+ MonitorAutoLock lock(*mChan->mMonitor);
+
+ if (needOpen) {
+ // Transport::Connect() has not been called. Call it so
+ // we start polling our pipe and processing outgoing
+ // messages.
+ mIOLoop->PostTask(NewNonOwningRunnableMethod(this, &ProcessLink::OnChannelOpened));
+ } else {
+ // Transport::Connect() has already been called. Take
+ // over the channel from the previous listener and process
+ // any queued messages.
+ mIOLoop->PostTask(NewNonOwningRunnableMethod(this, &ProcessLink::OnTakeConnectedChannel));
+ }
+
+ // Should not wait here if something goes wrong with the channel.
+ while (!mChan->Connected() && mChan->mChannelState != ChannelError) {
+ mChan->mMonitor->Wait();
+ }
+ }
+}
+
+void
+ProcessLink::EchoMessage(Message *msg)
+{
+ mChan->AssertWorkerThread();
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ mIOLoop->PostTask(NewNonOwningRunnableMethod<Message*>(this, &ProcessLink::OnEchoMessage, msg));
+ // OnEchoMessage takes ownership of |msg|
+}
+
+void
+ProcessLink::SendMessage(Message *msg)
+{
+ if (msg->size() > IPC::Channel::kMaximumMessageSize) {
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageName"), nsDependentCString(msg->name()));
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageSize"), nsPrintfCString("%d", msg->size()));
+#endif
+ MOZ_CRASH("IPC message size is too large");
+ }
+
+ mChan->AssertWorkerThread();
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ mIOLoop->PostTask(NewNonOwningRunnableMethod<Message*>(mTransport, &Transport::Send, msg));
+}
+
+void
+ProcessLink::SendClose()
+{
+ mChan->AssertWorkerThread();
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ mIOLoop->PostTask(NewNonOwningRunnableMethod(this, &ProcessLink::OnCloseChannel));
+}
+
+ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan)
+ : MessageLink(aChan),
+ mTargetChan(aTargetChan)
+{
+}
+
+ThreadLink::~ThreadLink()
+{
+ MOZ_ASSERT(mChan);
+ MOZ_ASSERT(mChan->mMonitor);
+ MonitorAutoLock lock(*mChan->mMonitor);
+
+ // Bug 848949: We need to prevent the other side
+ // from sending us any more messages to avoid Use-After-Free.
+ // The setup here is as shown:
+ //
+ // (Us) (Them)
+ // MessageChannel MessageChannel
+ // | ^ \ / ^ |
+ // | | X | |
+ // v | / \ | v
+ // ThreadLink ThreadLink
+ //
+ // We want to null out the diagonal link from their ThreadLink
+ // to our MessageChannel. Note that we must hold the monitor so
+ // that we do this atomically with respect to them trying to send
+ // us a message. Since the channels share the same monitor this
+ // also protects against the two ~ThreadLink() calls racing.
+ if (mTargetChan) {
+ MOZ_ASSERT(mTargetChan->mLink);
+ static_cast<ThreadLink*>(mTargetChan->mLink)->mTargetChan = nullptr;
+ }
+ mTargetChan = nullptr;
+}
+
+void
+ThreadLink::EchoMessage(Message *msg)
+{
+ mChan->AssertWorkerThread();
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ mChan->OnMessageReceivedFromLink(Move(*msg));
+ delete msg;
+}
+
+void
+ThreadLink::SendMessage(Message *msg)
+{
+ mChan->AssertWorkerThread();
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ if (mTargetChan)
+ mTargetChan->OnMessageReceivedFromLink(Move(*msg));
+ delete msg;
+}
+
+void
+ThreadLink::SendClose()
+{
+ mChan->AssertWorkerThread();
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ mChan->mChannelState = ChannelClosed;
+
+ // In a ProcessLink, we would close our half the channel. This
+ // would show up on the other side as an error on the I/O thread.
+ // The I/O thread would then invoke OnChannelErrorFromLink().
+ // As usual, we skip that process and just invoke the
+ // OnChannelErrorFromLink() method directly.
+ if (mTargetChan)
+ mTargetChan->OnChannelErrorFromLink();
+}
+
+bool
+ThreadLink::Unsound_IsClosed() const
+{
+ MonitorAutoLock lock(*mChan->mMonitor);
+ return mChan->mChannelState == ChannelClosed;
+}
+
+uint32_t
+ThreadLink::Unsound_NumQueuedMessages() const
+{
+ // ThreadLinks don't have a message queue.
+ return 0;
+}
+
+//
+// The methods below run in the context of the IO thread
+//
+
+void
+ProcessLink::OnMessageReceived(Message&& msg)
+{
+ AssertIOThread();
+ NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
+ MonitorAutoLock lock(*mChan->mMonitor);
+ mChan->OnMessageReceivedFromLink(Move(msg));
+}
+
+void
+ProcessLink::OnEchoMessage(Message* msg)
+{
+ AssertIOThread();
+ OnMessageReceived(Move(*msg));
+ delete msg;
+}
+
+void
+ProcessLink::OnChannelOpened()
+{
+ AssertIOThread();
+
+ {
+ MonitorAutoLock lock(*mChan->mMonitor);
+
+ mExistingListener = mTransport->set_listener(this);
+#ifdef DEBUG
+ if (mExistingListener) {
+ queue<Message> pending;
+ mExistingListener->GetQueuedMessages(pending);
+ MOZ_ASSERT(pending.empty());
+ }
+#endif // DEBUG
+
+ mChan->mChannelState = ChannelOpening;
+ lock.Notify();
+ }
+ /*assert*/mTransport->Connect();
+}
+
+void
+ProcessLink::OnTakeConnectedChannel()
+{
+ AssertIOThread();
+
+ queue<Message> pending;
+ {
+ MonitorAutoLock lock(*mChan->mMonitor);
+
+ mChan->mChannelState = ChannelConnected;
+
+ mExistingListener = mTransport->set_listener(this);
+ if (mExistingListener) {
+ mExistingListener->GetQueuedMessages(pending);
+ }
+ lock.Notify();
+ }
+
+ // Dispatch whatever messages the previous listener had queued up.
+ while (!pending.empty()) {
+ OnMessageReceived(Move(pending.front()));
+ pending.pop();
+ }
+}
+
+void
+ProcessLink::OnChannelConnected(int32_t peer_pid)
+{
+ AssertIOThread();
+
+ bool notifyChannel = false;
+
+ {
+ MonitorAutoLock lock(*mChan->mMonitor);
+ // Only update channel state if its still thinks its opening. Do not
+ // force it into connected if it has errored out, started closing, etc.
+ if (mChan->mChannelState == ChannelOpening) {
+ mChan->mChannelState = ChannelConnected;
+ mChan->mMonitor->Notify();
+ notifyChannel = true;
+ }
+ }
+
+ if (mExistingListener)
+ mExistingListener->OnChannelConnected(peer_pid);
+
+ if (notifyChannel) {
+ mChan->OnChannelConnected(peer_pid);
+ }
+}
+
+void
+ProcessLink::OnChannelError()
+{
+ AssertIOThread();
+
+ MonitorAutoLock lock(*mChan->mMonitor);
+
+ MOZ_ALWAYS_TRUE(this == mTransport->set_listener(mExistingListener));
+
+ mChan->OnChannelErrorFromLink();
+}
+
+void
+ProcessLink::OnCloseChannel()
+{
+ AssertIOThread();
+
+ mTransport->Close();
+
+ MonitorAutoLock lock(*mChan->mMonitor);
+
+ DebugOnly<IPC::Channel::Listener*> previousListener =
+ mTransport->set_listener(mExistingListener);
+
+ // OnChannelError may have reset the listener already.
+ MOZ_ASSERT(previousListener == this ||
+ previousListener == mExistingListener);
+
+ mChan->mChannelState = ChannelClosed;
+ mChan->mMonitor->Notify();
+}
+
+bool
+ProcessLink::Unsound_IsClosed() const
+{
+ return mTransport->Unsound_IsClosed();
+}
+
+uint32_t
+ProcessLink::Unsound_NumQueuedMessages() const
+{
+ return mTransport->Unsound_NumQueuedMessages();
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/MessageLink.h b/ipc/glue/MessageLink.h
new file mode 100644
index 000000000..d0e05fc63
--- /dev/null
+++ b/ipc/glue/MessageLink.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 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/. */
+
+#ifndef ipc_glue_MessageLink_h
+#define ipc_glue_MessageLink_h 1
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+
+#include "mozilla/WeakPtr.h"
+#include "mozilla/ipc/Transport.h"
+
+namespace mozilla {
+namespace ipc {
+
+class MessageChannel;
+
+struct HasResultCodes
+{
+ enum Result {
+ MsgProcessed,
+ MsgDropped,
+ MsgNotKnown,
+ MsgNotAllowed,
+ MsgPayloadError,
+ MsgProcessingError,
+ MsgRouteError,
+ MsgValueError
+ };
+};
+
+enum Side {
+ ParentSide,
+ ChildSide,
+ UnknownSide
+};
+
+class MessageLink
+{
+ public:
+ typedef IPC::Message Message;
+
+ explicit MessageLink(MessageChannel *aChan);
+ virtual ~MessageLink();
+
+ // n.b.: These methods all require that the channel monitor is
+ // held when they are invoked.
+ virtual void EchoMessage(Message *msg) = 0;
+ virtual void SendMessage(Message *msg) = 0;
+ virtual void SendClose() = 0;
+
+ virtual bool Unsound_IsClosed() const = 0;
+ virtual uint32_t Unsound_NumQueuedMessages() const = 0;
+
+ protected:
+ MessageChannel *mChan;
+};
+
+class ProcessLink
+ : public MessageLink,
+ public Transport::Listener
+{
+ void OnCloseChannel();
+ void OnChannelOpened();
+ void OnTakeConnectedChannel();
+ void OnEchoMessage(Message* msg);
+
+ void AssertIOThread() const
+ {
+ MOZ_ASSERT(mIOLoop == MessageLoop::current(),
+ "not on I/O thread!");
+ }
+
+ public:
+ explicit ProcessLink(MessageChannel *chan);
+ virtual ~ProcessLink();
+
+ // The ProcessLink will register itself as the IPC::Channel::Listener on the
+ // transport passed here. If the transport already has a listener registered
+ // then a listener chain will be established (the ProcessLink listener
+ // methods will be called first and may call some methods on the original
+ // listener as well). Once the channel is closed (either via normal shutdown
+ // or a pipe error) the chain will be destroyed and the original listener
+ // will again be registered.
+ void Open(Transport* aTransport, MessageLoop *aIOLoop, Side aSide);
+
+ // Run on the I/O thread, only when using inter-process link.
+ // These methods acquire the monitor and forward to the
+ // similarly named methods in AsyncChannel below
+ // (OnMessageReceivedFromLink(), etc)
+ virtual void OnMessageReceived(Message&& msg) override;
+ virtual void OnChannelConnected(int32_t peer_pid) override;
+ virtual void OnChannelError() override;
+
+ virtual void EchoMessage(Message *msg) override;
+ virtual void SendMessage(Message *msg) override;
+ virtual void SendClose() override;
+
+ virtual bool Unsound_IsClosed() const override;
+ virtual uint32_t Unsound_NumQueuedMessages() const override;
+
+ protected:
+ Transport* mTransport;
+ MessageLoop* mIOLoop; // thread where IO happens
+ Transport::Listener* mExistingListener; // channel's previous listener
+};
+
+class ThreadLink : public MessageLink
+{
+ public:
+ ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan);
+ virtual ~ThreadLink();
+
+ virtual void EchoMessage(Message *msg) override;
+ virtual void SendMessage(Message *msg) override;
+ virtual void SendClose() override;
+
+ virtual bool Unsound_IsClosed() const override;
+ virtual uint32_t Unsound_NumQueuedMessages() const override;
+
+ protected:
+ MessageChannel* mTargetChan;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // ifndef ipc_glue_MessageLink_h
+
diff --git a/ipc/glue/MessagePump.cpp b/ipc/glue/MessagePump.cpp
new file mode 100644
index 000000000..15c17b8f4
--- /dev/null
+++ b/ipc/glue/MessagePump.cpp
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "MessagePump.h"
+
+#include "nsIRunnable.h"
+#include "nsIThread.h"
+#include "nsITimer.h"
+#include "nsICancelableRunnable.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_nsautorelease_pool.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDebug.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsTimerImpl.h"
+#include "nsXULAppAPI.h"
+#include "prthread.h"
+
+using base::TimeTicks;
+using namespace mozilla::ipc;
+
+NS_DEFINE_NAMED_CID(NS_TIMER_CID);
+
+#ifdef DEBUG
+static MessagePump::Delegate* gFirstDelegate;
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+class DoWorkRunnable final : public CancelableRunnable,
+ public nsITimerCallback
+{
+public:
+ explicit DoWorkRunnable(MessagePump* aPump)
+ : mPump(aPump)
+ {
+ MOZ_ASSERT(aPump);
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIRUNNABLE
+ NS_DECL_NSITIMERCALLBACK
+ nsresult Cancel() override;
+
+private:
+ ~DoWorkRunnable()
+ { }
+
+ MessagePump* mPump;
+ // DoWorkRunnable is designed as a stateless singleton. Do not add stateful
+ // members here!
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+MessagePump::MessagePump(nsIThread* aThread)
+: mThread(aThread)
+{
+ mDoWorkEvent = new DoWorkRunnable(this);
+}
+
+MessagePump::~MessagePump()
+{
+}
+
+void
+MessagePump::Run(MessagePump::Delegate* aDelegate)
+{
+ MOZ_ASSERT(keep_running_);
+ MOZ_RELEASE_ASSERT(NS_IsMainThread(),
+ "Use mozilla::ipc::MessagePumpForNonMainThreads instead!");
+ MOZ_RELEASE_ASSERT(!mThread);
+
+ nsIThread* thisThread = NS_GetCurrentThread();
+ MOZ_ASSERT(thisThread);
+
+ mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
+ MOZ_ASSERT(mDelayedWorkTimer);
+
+ base::ScopedNSAutoreleasePool autoReleasePool;
+
+ for (;;) {
+ autoReleasePool.Recycle();
+
+ bool did_work = NS_ProcessNextEvent(thisThread, false) ? true : false;
+ if (!keep_running_)
+ break;
+
+ // NB: it is crucial *not* to directly call |aDelegate->DoWork()|
+ // here. To ensure that MessageLoop tasks and XPCOM events have
+ // equal priority, we sensitively rely on processing exactly one
+ // Task per DoWorkRunnable XPCOM event.
+
+ did_work |= aDelegate->DoDelayedWork(&delayed_work_time_);
+
+if (did_work && delayed_work_time_.is_null())
+ mDelayedWorkTimer->Cancel();
+
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ did_work = aDelegate->DoIdleWork();
+ if (!keep_running_)
+ break;
+
+ if (did_work)
+ continue;
+
+ // This will either sleep or process an event.
+ NS_ProcessNextEvent(thisThread, true);
+ }
+
+ mDelayedWorkTimer->Cancel();
+
+ keep_running_ = true;
+}
+
+void
+MessagePump::ScheduleWork()
+{
+ // Make sure the event loop wakes up.
+ if (mThread) {
+ mThread->Dispatch(mDoWorkEvent, NS_DISPATCH_NORMAL);
+ } else {
+ // Some things (like xpcshell) don't use the app shell and so Run hasn't
+ // been called. We still need to wake up the main thread.
+ NS_DispatchToMainThread(mDoWorkEvent);
+ }
+ event_.Signal();
+}
+
+void
+MessagePump::ScheduleWorkForNestedLoop()
+{
+ // This method is called when our MessageLoop has just allowed
+ // nested tasks. In our setup, whenever that happens we know that
+ // DoWork() will be called "soon", so there's no need to pay the
+ // cost of what will be a no-op nsThread::Dispatch(mDoWorkEvent).
+}
+
+void
+MessagePump::ScheduleDelayedWork(const base::TimeTicks& aDelayedTime)
+{
+ // To avoid racing on mDelayedWorkTimer, we need to be on the same thread as
+ // ::Run().
+ MOZ_RELEASE_ASSERT(NS_GetCurrentThread() == mThread ||
+ (!mThread && NS_IsMainThread()));
+
+ if (!mDelayedWorkTimer) {
+ mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
+ if (!mDelayedWorkTimer) {
+ // Called before XPCOM has started up? We can't do this correctly.
+ NS_WARNING("Delayed task might not run!");
+ delayed_work_time_ = aDelayedTime;
+ return;
+ }
+ }
+
+ if (!delayed_work_time_.is_null()) {
+ mDelayedWorkTimer->Cancel();
+ }
+
+ delayed_work_time_ = aDelayedTime;
+
+ // TimeDelta's constructor initializes to 0
+ base::TimeDelta delay;
+ if (aDelayedTime > base::TimeTicks::Now())
+ delay = aDelayedTime - base::TimeTicks::Now();
+
+ uint32_t delayMS = uint32_t(delay.InMilliseconds());
+ mDelayedWorkTimer->InitWithCallback(mDoWorkEvent, delayMS,
+ nsITimer::TYPE_ONE_SHOT);
+}
+
+nsIEventTarget*
+MessagePump::GetXPCOMThread()
+{
+ if (mThread) {
+ return mThread;
+ }
+
+ // Main thread
+ nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+ return mainThread;
+}
+
+void
+MessagePump::DoDelayedWork(base::MessagePump::Delegate* aDelegate)
+{
+ aDelegate->DoDelayedWork(&delayed_work_time_);
+ if (!delayed_work_time_.is_null()) {
+ ScheduleDelayedWork(delayed_work_time_);
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(DoWorkRunnable, CancelableRunnable,
+ nsITimerCallback)
+
+NS_IMETHODIMP
+DoWorkRunnable::Run()
+{
+ MessageLoop* loop = MessageLoop::current();
+ MOZ_ASSERT(loop);
+
+ bool nestableTasksAllowed = loop->NestableTasksAllowed();
+
+ // MessageLoop::RunTask() disallows nesting, but our Frankenventloop will
+ // always dispatch DoWork() below from what looks to MessageLoop like a nested
+ // context. So we unconditionally allow nesting here.
+ loop->SetNestableTasksAllowed(true);
+ loop->DoWork();
+ loop->SetNestableTasksAllowed(nestableTasksAllowed);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DoWorkRunnable::Notify(nsITimer* aTimer)
+{
+ MessageLoop* loop = MessageLoop::current();
+ MOZ_ASSERT(loop);
+
+ bool nestableTasksAllowed = loop->NestableTasksAllowed();
+ loop->SetNestableTasksAllowed(true);
+ mPump->DoDelayedWork(loop);
+ loop->SetNestableTasksAllowed(nestableTasksAllowed);
+
+ return NS_OK;
+}
+
+nsresult
+DoWorkRunnable::Cancel()
+{
+ // Workers require cancelable runnables, but we can't really cancel cleanly
+ // here. If we don't process this runnable then we will leave something
+ // unprocessed in the message_loop. Therefore, eagerly complete our work
+ // instead by immediately calling Run(). Run() should be called separately
+ // after this. Unfortunately we cannot use flags to verify this because
+ // DoWorkRunnable is a stateless singleton that can be in the event queue
+ // multiple times simultaneously.
+ MOZ_ALWAYS_SUCCEEDS(Run());
+ return NS_OK;
+}
+
+void
+MessagePumpForChildProcess::Run(base::MessagePump::Delegate* aDelegate)
+{
+ if (mFirstRun) {
+ MOZ_ASSERT(aDelegate && !gFirstDelegate);
+#ifdef DEBUG
+ gFirstDelegate = aDelegate;
+#endif
+
+ mFirstRun = false;
+ if (NS_FAILED(XRE_RunAppShell())) {
+ NS_WARNING("Failed to run app shell?!");
+ }
+
+ MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
+#ifdef DEBUG
+ gFirstDelegate = nullptr;
+#endif
+
+ return;
+ }
+
+ MOZ_ASSERT(aDelegate && aDelegate == gFirstDelegate);
+
+ // We can get to this point in startup with Tasks in our loop's
+ // incoming_queue_ or pending_queue_, but without a matching
+ // DoWorkRunnable(). In MessagePump::Run() above, we sensitively
+ // depend on *not* directly calling delegate->DoWork(), because that
+ // prioritizes Tasks above XPCOM events. However, from this point
+ // forward, any Task posted to our loop is guaranteed to have a
+ // DoWorkRunnable enqueued for it.
+ //
+ // So we just flush the pending work here and move on.
+ MessageLoop* loop = MessageLoop::current();
+ bool nestableTasksAllowed = loop->NestableTasksAllowed();
+ loop->SetNestableTasksAllowed(true);
+
+ while (aDelegate->DoWork());
+
+ loop->SetNestableTasksAllowed(nestableTasksAllowed);
+
+ // Really run.
+ mozilla::ipc::MessagePump::Run(aDelegate);
+}
+
+void
+MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate* aDelegate)
+{
+ MOZ_ASSERT(keep_running_);
+ MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
+
+ nsIThread* thread = NS_GetCurrentThread();
+ MOZ_RELEASE_ASSERT(mThread == thread);
+
+ mDelayedWorkTimer = do_CreateInstance(kNS_TIMER_CID);
+ MOZ_ASSERT(mDelayedWorkTimer);
+
+ if (NS_FAILED(mDelayedWorkTimer->SetTarget(thread))) {
+ MOZ_CRASH("Failed to set timer target!");
+ }
+
+ // Chromium event notifications to be processed will be received by this
+ // event loop as a DoWorkRunnables via ScheduleWork. Chromium events that
+ // were received before our thread is valid, however, will not generate
+ // runnable wrappers. We must process any of these before we enter this
+ // loop, or we will forever have unprocessed chromium messages in our queue.
+ //
+ // Note we would like to request a flush of the chromium event queue
+ // using a runnable on the xpcom side, but some thread implementations
+ // (dom workers) get cranky if we call ScheduleWork here (ScheduleWork
+ // calls dispatch on mThread) before the thread processes an event. As
+ // such, clear the queue manually.
+ while (aDelegate->DoWork()) {
+ }
+
+ base::ScopedNSAutoreleasePool autoReleasePool;
+ for (;;) {
+ autoReleasePool.Recycle();
+
+ bool didWork = NS_ProcessNextEvent(thread, false) ? true : false;
+ if (!keep_running_) {
+ break;
+ }
+
+ didWork |= aDelegate->DoDelayedWork(&delayed_work_time_);
+
+ if (didWork && delayed_work_time_.is_null()) {
+ mDelayedWorkTimer->Cancel();
+ }
+
+ if (!keep_running_) {
+ break;
+ }
+
+ if (didWork) {
+ continue;
+ }
+
+ DebugOnly<bool> didIdleWork = aDelegate->DoIdleWork();
+ MOZ_ASSERT(!didIdleWork);
+ if (!keep_running_) {
+ break;
+ }
+
+ if (didWork) {
+ continue;
+ }
+
+ // This will either sleep or process an event.
+ NS_ProcessNextEvent(thread, true);
+ }
+
+ mDelayedWorkTimer->Cancel();
+
+ keep_running_ = true;
+}
+
+#if defined(XP_WIN)
+
+NS_IMPL_QUERY_INTERFACE(MessagePumpForNonMainUIThreads, nsIThreadObserver)
+
+#define CHECK_QUIT_STATE { if (state_->should_quit) { break; } }
+
+void
+MessagePumpForNonMainUIThreads::DoRunLoop()
+{
+ MOZ_RELEASE_ASSERT(!NS_IsMainThread(), "Use mozilla::ipc::MessagePump instead!");
+
+ // If this is a chromium thread and no nsThread is associated
+ // with it, this call will create a new nsThread.
+ nsIThread* thread = NS_GetCurrentThread();
+ MOZ_ASSERT(thread);
+
+ // Set the main thread observer so we can wake up when
+ // xpcom events need to get processed.
+ nsCOMPtr<nsIThreadInternal> ti(do_QueryInterface(thread));
+ MOZ_ASSERT(ti);
+ ti->SetObserver(this);
+
+ base::ScopedNSAutoreleasePool autoReleasePool;
+ for (;;) {
+ autoReleasePool.Recycle();
+
+ bool didWork = NS_ProcessNextEvent(thread, false);
+
+ didWork |= ProcessNextWindowsMessage();
+ CHECK_QUIT_STATE
+
+ didWork |= state_->delegate->DoWork();
+ CHECK_QUIT_STATE
+
+ didWork |= state_->delegate->DoDelayedWork(&delayed_work_time_);
+ if (didWork && delayed_work_time_.is_null()) {
+ KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
+ }
+ CHECK_QUIT_STATE
+
+ if (didWork) {
+ continue;
+ }
+
+ DebugOnly<bool> didIdleWork = state_->delegate->DoIdleWork();
+ MOZ_ASSERT(!didIdleWork);
+ CHECK_QUIT_STATE
+
+ SetInWait();
+ bool hasWork = NS_HasPendingEvents(thread);
+ if (didWork || hasWork) {
+ ClearInWait();
+ continue;
+ }
+ WaitForWork(); // Calls MsgWaitForMultipleObjectsEx(QS_ALLINPUT)
+ ClearInWait();
+ }
+
+ ClearInWait();
+
+ ti->SetObserver(nullptr);
+}
+
+NS_IMETHODIMP
+MessagePumpForNonMainUIThreads::OnDispatchedEvent(nsIThreadInternal *thread)
+{
+ // If our thread is sleeping in DoRunLoop's call to WaitForWork() and an
+ // event posts to the nsIThread event queue - break our thread out of
+ // chromium's WaitForWork.
+ if (GetInWait()) {
+ ScheduleWork();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal *thread,
+ bool mayWait)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal *thread,
+ bool eventWasProcessed)
+{
+ return NS_OK;
+}
+
+#endif // XP_WIN
diff --git a/ipc/glue/MessagePump.h b/ipc/glue/MessagePump.h
new file mode 100644
index 000000000..3e48624ed
--- /dev/null
+++ b/ipc/glue/MessagePump.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __IPC_GLUE_MESSAGEPUMP_H__
+#define __IPC_GLUE_MESSAGEPUMP_H__
+
+#include "base/message_pump_default.h"
+#if defined(XP_WIN)
+#include "base/message_pump_win.h"
+#endif
+
+#include "base/time.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "nsCOMPtr.h"
+#include "nsIThreadInternal.h"
+
+class nsIThread;
+class nsITimer;
+
+namespace mozilla {
+namespace ipc {
+
+class DoWorkRunnable;
+
+class MessagePump : public base::MessagePumpDefault
+{
+ friend class DoWorkRunnable;
+
+public:
+ explicit MessagePump(nsIThread* aThread);
+
+ // From base::MessagePump.
+ virtual void
+ Run(base::MessagePump::Delegate* aDelegate) override;
+
+ // From base::MessagePump.
+ virtual void
+ ScheduleWork() override;
+
+ // From base::MessagePump.
+ virtual void
+ ScheduleWorkForNestedLoop() override;
+
+ // From base::MessagePump.
+ virtual void
+ ScheduleDelayedWork(const base::TimeTicks& aDelayedWorkTime) override;
+
+ virtual nsIEventTarget*
+ GetXPCOMThread() override;
+
+protected:
+ virtual ~MessagePump();
+
+private:
+ // Only called by DoWorkRunnable.
+ void DoDelayedWork(base::MessagePump::Delegate* aDelegate);
+
+protected:
+ nsIThread* mThread;
+
+ // mDelayedWorkTimer and mThread are set in Run() by this class or its
+ // subclasses.
+ nsCOMPtr<nsITimer> mDelayedWorkTimer;
+
+private:
+ // Only accessed by this class.
+ RefPtr<DoWorkRunnable> mDoWorkEvent;
+};
+
+class MessagePumpForChildProcess final: public MessagePump
+{
+public:
+ MessagePumpForChildProcess()
+ : MessagePump(nullptr),
+ mFirstRun(true)
+ { }
+
+ virtual void Run(base::MessagePump::Delegate* aDelegate) override;
+
+private:
+ ~MessagePumpForChildProcess()
+ { }
+
+ bool mFirstRun;
+};
+
+class MessagePumpForNonMainThreads final : public MessagePump
+{
+public:
+ explicit MessagePumpForNonMainThreads(nsIThread* aThread)
+ : MessagePump(aThread)
+ { }
+
+ virtual void Run(base::MessagePump::Delegate* aDelegate) override;
+
+private:
+ ~MessagePumpForNonMainThreads()
+ { }
+};
+
+#if defined(XP_WIN)
+// Extends the TYPE_UI message pump to process xpcom events. Currently only
+// implemented for Win.
+class MessagePumpForNonMainUIThreads final:
+ public base::MessagePumpForUI,
+ public nsIThreadObserver
+{
+public:
+ // We don't want xpcom refing, chromium controls our lifetime via
+ // RefCountedThreadSafe.
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override {
+ return 2;
+ }
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override {
+ return 1;
+ }
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+
+ NS_DECL_NSITHREADOBSERVER
+
+public:
+ explicit MessagePumpForNonMainUIThreads(nsIThread* aThread) :
+ mInWait(false),
+ mWaitLock("mInWait")
+ {
+ }
+
+ // The main run loop for this thread.
+ virtual void DoRunLoop() override;
+
+ virtual nsIEventTarget*
+ GetXPCOMThread() override
+ {
+ return nullptr; // not sure what to do with this one
+ }
+
+protected:
+ void SetInWait() {
+ MutexAutoLock lock(mWaitLock);
+ mInWait = true;
+ }
+
+ void ClearInWait() {
+ MutexAutoLock lock(mWaitLock);
+ mInWait = false;
+ }
+
+ bool GetInWait() {
+ MutexAutoLock lock(mWaitLock);
+ return mInWait;
+ }
+
+private:
+ ~MessagePumpForNonMainUIThreads()
+ {
+ }
+
+ bool mInWait;
+ mozilla::Mutex mWaitLock;
+};
+#endif // defined(XP_WIN)
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* __IPC_GLUE_MESSAGEPUMP_H__ */
diff --git a/ipc/glue/Neutering.h b/ipc/glue/Neutering.h
new file mode 100644
index 000000000..d4ca816da
--- /dev/null
+++ b/ipc/glue/Neutering.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_Neutering_h
+#define mozilla_ipc_Neutering_h
+
+#include "mozilla/GuardObjects.h"
+
+/**
+ * This header declares RAII wrappers for Window neutering. See
+ * WindowsMessageLoop.cpp for more details.
+ */
+
+namespace mozilla {
+namespace ipc {
+
+/**
+ * This class is a RAII wrapper around Window neutering. As long as a
+ * NeuteredWindowRegion object is instantiated, Win32 windows belonging to the
+ * current thread will be neutered. It is safe to nest multiple instances of
+ * this class.
+ */
+class MOZ_RAII NeuteredWindowRegion
+{
+public:
+ explicit NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+ ~NeuteredWindowRegion();
+
+ /**
+ * This function clears any backlog of nonqueued messages that are pending for
+ * the current thread.
+ */
+ void PumpOnce();
+
+private:
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+ bool mNeuteredByThis;
+};
+
+/**
+ * This class is analagous to MutexAutoUnlock for Mutex; it is an RAII class
+ * that is to be instantiated within a NeuteredWindowRegion, thus temporarily
+ * disabling neutering for the remainder of its enclosing block.
+ * @see NeuteredWindowRegion
+ */
+class MOZ_RAII DeneuteredWindowRegion
+{
+public:
+ DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
+ ~DeneuteredWindowRegion();
+
+private:
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+ bool mReneuter;
+};
+
+class MOZ_RAII SuppressedNeuteringRegion
+{
+public:
+ SuppressedNeuteringRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
+ ~SuppressedNeuteringRegion();
+
+ static inline bool IsNeuteringSuppressed() { return sSuppressNeutering; }
+
+private:
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+ bool mReenable;
+
+ static bool sSuppressNeutering;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_Neutering_h
+
diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl
new file mode 100644
index 000000000..eacb42769
--- /dev/null
+++ b/ipc/glue/PBackground.ipdl
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 PAsmJSCacheEntry;
+include protocol PBackgroundIDBFactory;
+include protocol PBackgroundIndexedDBUtils;
+include protocol PBackgroundTest;
+include protocol PBlob;
+include protocol PBroadcastChannel;
+include protocol PCache;
+include protocol PCacheStorage;
+include protocol PCacheStreamControl;
+include protocol PFileDescriptorSet;
+include protocol PFileSystemRequest;
+include protocol PGamepadEventChannel;
+include protocol PGamepadTestChannel;
+include protocol PMessagePort;
+include protocol PCameras;
+include protocol PQuota;
+include protocol PSendStream;
+include protocol PServiceWorkerManager;
+include protocol PUDPSocket;
+include protocol PVsync;
+
+include DOMTypes;
+include PBackgroundSharedTypes;
+include PBackgroundIDBSharedTypes;
+include PFileSystemParams;
+include ProtocolTypes;
+
+include "mozilla/dom/cache/IPCUtils.h";
+
+using mozilla::dom::cache::Namespace
+ from "mozilla/dom/cache/Types.h";
+
+using mozilla::dom::asmjscache::OpenMode
+ from "mozilla/dom/asmjscache/AsmJSCache.h";
+
+using mozilla::dom::asmjscache::WriteParams
+ from "mozilla/dom/asmjscache/AsmJSCache.h";
+
+namespace mozilla {
+namespace ipc {
+
+sync protocol PBackground
+{
+ manages PAsmJSCacheEntry;
+ manages PBackgroundIDBFactory;
+ manages PBackgroundIndexedDBUtils;
+ manages PBackgroundTest;
+ manages PBlob;
+ manages PBroadcastChannel;
+ manages PCache;
+ manages PCacheStorage;
+ manages PCacheStreamControl;
+ manages PFileDescriptorSet;
+ manages PFileSystemRequest;
+ manages PGamepadEventChannel;
+ manages PGamepadTestChannel;
+ manages PMessagePort;
+ manages PCameras;
+ manages PQuota;
+ manages PSendStream;
+ manages PServiceWorkerManager;
+ manages PUDPSocket;
+ manages PVsync;
+
+parent:
+ // Only called at startup during mochitests to check the basic infrastructure.
+ async PBackgroundTest(nsCString testArg);
+
+ async PBackgroundIDBFactory(LoggingInfo loggingInfo);
+
+ async PBackgroundIndexedDBUtils();
+
+ // Use only for testing!
+ async FlushPendingFileDeletions();
+
+ async PVsync();
+
+ async PCameras();
+
+ async PUDPSocket(OptionalPrincipalInfo pInfo, nsCString filter);
+ async PBroadcastChannel(PrincipalInfo pInfo, nsCString origin, nsString channel);
+
+ async PServiceWorkerManager();
+
+ async ShutdownServiceWorkerRegistrar();
+
+ async PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
+
+ async PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
+
+ async PSendStream();
+
+ async MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
+
+ async PAsmJSCacheEntry(OpenMode openMode,
+ WriteParams write,
+ PrincipalInfo principalInfo);
+
+ async PQuota();
+
+ async PFileSystemRequest(FileSystemParams params);
+
+ async PGamepadEventChannel();
+
+ async PGamepadTestChannel();
+
+child:
+ async PCache();
+ async PCacheStreamControl();
+
+both:
+ async PBlob(BlobConstructorParams params);
+
+ async PFileDescriptorSet(FileDescriptor fd);
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/PBackgroundSharedTypes.ipdlh b/ipc/glue/PBackgroundSharedTypes.ipdlh
new file mode 100644
index 000000000..ccca1decd
--- /dev/null
+++ b/ipc/glue/PBackgroundSharedTypes.ipdlh
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+using mozilla::PrincipalOriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace ipc {
+
+union ContentPrincipalInfoOriginNoSuffix
+{
+ nsCString;
+ void_t;
+};
+
+struct ContentPrincipalInfo
+{
+ PrincipalOriginAttributes attrs;
+
+ // nsIPrincipal.originNoSuffix can fail. In case this happens, this value
+ // will be set to void_t. So far, this is used only for dom/media.
+ // It will be removed in bug 1347817.
+ ContentPrincipalInfoOriginNoSuffix originNoSuffix;
+
+ nsCString spec;
+};
+
+struct SystemPrincipalInfo
+{ };
+
+struct NullPrincipalInfo
+{
+ PrincipalOriginAttributes attrs;
+};
+
+struct ExpandedPrincipalInfo
+{
+ PrincipalOriginAttributes attrs;
+ PrincipalInfo[] whitelist;
+};
+
+union PrincipalInfo
+{
+ ContentPrincipalInfo;
+ SystemPrincipalInfo;
+ NullPrincipalInfo;
+ ExpandedPrincipalInfo;
+};
+
+union OptionalPrincipalInfo
+{
+ void_t;
+ PrincipalInfo;
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/PBackgroundTest.ipdl b/ipc/glue/PBackgroundTest.ipdl
new file mode 100644
index 000000000..527cd4ce7
--- /dev/null
+++ b/ipc/glue/PBackgroundTest.ipdl
@@ -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/. */
+
+include protocol PBackground;
+
+namespace mozilla {
+namespace ipc {
+
+// This is a very simple testing protocol that is only used during mochitests.
+protocol PBackgroundTest
+{
+ manager PBackground;
+
+child:
+ async __delete__(nsCString testArg);
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/PFileDescriptorSet.ipdl b/ipc/glue/PFileDescriptorSet.ipdl
new file mode 100644
index 000000000..763f72778
--- /dev/null
+++ b/ipc/glue/PFileDescriptorSet.ipdl
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 PContent;
+include protocol PContentBridge;
+
+namespace mozilla {
+namespace ipc {
+
+protocol PFileDescriptorSet
+{
+ manager PBackground or PContent or PContentBridge;
+
+both:
+ async AddFileDescriptor(FileDescriptor fd);
+
+ async __delete__();
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/PSendStream.ipdl b/ipc/glue/PSendStream.ipdl
new file mode 100644
index 000000000..8f171672b
--- /dev/null
+++ b/ipc/glue/PSendStream.ipdl
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 PContent;
+include protocol PContentBridge;
+
+namespace mozilla {
+namespace ipc {
+
+protocol PSendStream
+{
+ manager PBackground or PContent or PContentBridge;
+
+parent:
+ async Buffer(nsCString aBuffer);
+ async Close(nsresult aRv);
+
+child:
+ // The parent side has hit an error condition and has requested the child
+ // actor issue a Close() message. The close must be initiated by the child
+ // to avoid racing with an in-flight Buffer() message.
+ async RequestClose(nsresult aRv);
+
+ // Stream is always destroyed from the parent side. This occurs if the
+ // parent encounters an error while writing to its pipe or if the child
+ // signals the stream should close by SendClose().
+ async __delete__();
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/ProcessChild.cpp b/ipc/glue/ProcessChild.cpp
new file mode 100644
index 000000000..2a84d5ed2
--- /dev/null
+++ b/ipc/glue/ProcessChild.cpp
@@ -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/. */
+
+#include "nsDebug.h"
+
+#ifdef XP_WIN
+#include <stdlib.h> // for _exit()
+#else
+#include <unistd.h> // for _exit()
+#endif
+
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/ipc/ProcessChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+ProcessChild* ProcessChild::gProcessChild;
+
+ProcessChild::ProcessChild(ProcessId aParentPid)
+ : ChildProcess(new IOThreadChild())
+ , mUILoop(MessageLoop::current())
+ , mParentPid(aParentPid)
+{
+ MOZ_ASSERT(mUILoop, "UILoop should be created by now");
+ MOZ_ASSERT(!gProcessChild, "should only be one ProcessChild");
+ gProcessChild = this;
+}
+
+ProcessChild::~ProcessChild()
+{
+ gProcessChild = nullptr;
+}
+
+/* static */ void
+ProcessChild::QuickExit()
+{
+#ifdef XP_WIN
+ // In bug 1254829, the destructor got called when dll got detached on windows,
+ // switch to TerminateProcess to bypass dll detach handler during the process
+ // termination.
+ TerminateProcess(GetCurrentProcess(), 0);
+#else
+ _exit(0);
+#endif
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/ProcessChild.h b/ipc/glue/ProcessChild.h
new file mode 100644
index 000000000..4d1d38659
--- /dev/null
+++ b/ipc/glue/ProcessChild.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_ProcessChild_h
+#define mozilla_ipc_ProcessChild_h
+
+#include "base/message_loop.h"
+#include "base/process.h"
+
+#include "chrome/common/child_process.h"
+
+// ProcessChild is the base class for all subprocesses of the main
+// browser process. Its code runs on the thread that started in
+// main().
+
+namespace mozilla {
+namespace ipc {
+
+class ProcessChild : public ChildProcess {
+protected:
+ typedef base::ProcessId ProcessId;
+
+public:
+ explicit ProcessChild(ProcessId aParentPid);
+ virtual ~ProcessChild();
+
+ virtual bool Init() = 0;
+ virtual void CleanUp()
+ { }
+
+ static MessageLoop* message_loop() {
+ return gProcessChild->mUILoop;
+ }
+
+ /**
+ * Exit *now*. Do not shut down XPCOM, do not pass Go, do not run
+ * static destructors, do not collect $200.
+ */
+ static void QuickExit();
+
+protected:
+ static ProcessChild* current() {
+ return gProcessChild;
+ }
+
+ ProcessId ParentPid() {
+ return mParentPid;
+ }
+
+private:
+ static ProcessChild* gProcessChild;
+
+ MessageLoop* mUILoop;
+ ProcessId mParentPid;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ProcessChild);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_ipc_ProcessChild_h
diff --git a/ipc/glue/ProcessUtils.h b/ipc/glue/ProcessUtils.h
new file mode 100644
index 000000000..2908d9876
--- /dev/null
+++ b/ipc/glue/ProcessUtils.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_ProcessUtils_h
+#define mozilla_ipc_ProcessUtils_h
+
+namespace mozilla {
+namespace ipc {
+
+// You probably should call ContentChild::SetProcessName instead of calling
+// this directly.
+void SetThisProcessName(const char *aName);
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // ifndef mozilla_ipc_ProcessUtils_h
+
diff --git a/ipc/glue/ProcessUtils_bsd.cpp b/ipc/glue/ProcessUtils_bsd.cpp
new file mode 100644
index 000000000..f6c7de227
--- /dev/null
+++ b/ipc/glue/ProcessUtils_bsd.cpp
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProcessUtils.h"
+
+#include <pthread.h>
+
+#if !defined(OS_NETBSD)
+#include <pthread_np.h>
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+void SetThisProcessName(const char *aName)
+{
+#if defined(OS_NETBSD)
+ pthread_setname_np(pthread_self(), "%s", (void *)aName);
+#else
+ pthread_set_name_np(pthread_self(), aName);
+#endif
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/ProcessUtils_linux.cpp b/ipc/glue/ProcessUtils_linux.cpp
new file mode 100644
index 000000000..d95527a4f
--- /dev/null
+++ b/ipc/glue/ProcessUtils_linux.cpp
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProcessUtils.h"
+
+#include "nsString.h"
+
+#include <sys/prctl.h>
+
+namespace mozilla {
+
+namespace ipc {
+
+void SetThisProcessName(const char *aName)
+{
+ prctl(PR_SET_NAME, (unsigned long)aName, 0uL, 0uL, 0uL);
+}
+
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/ProcessUtils_mac.mm b/ipc/glue/ProcessUtils_mac.mm
new file mode 100644
index 000000000..6c5738871
--- /dev/null
+++ b/ipc/glue/ProcessUtils_mac.mm
@@ -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/. */
+
+#include "ProcessUtils.h"
+
+#include "nsString.h"
+
+#include "mozilla/plugins/PluginUtilsOSX.h"
+
+namespace mozilla {
+namespace ipc {
+
+void SetThisProcessName(const char *aName)
+{
+ mozilla::plugins::PluginUtilsOSX::SetProcessName(aName);
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/ProcessUtils_none.cpp b/ipc/glue/ProcessUtils_none.cpp
new file mode 100644
index 000000000..721bd4a74
--- /dev/null
+++ b/ipc/glue/ProcessUtils_none.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "ProcessUtils.h"
+
+namespace mozilla {
+namespace ipc {
+
+void SetThisProcessName(const char *aString)
+{
+ (void)aString;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/ProtocolTypes.ipdlh b/ipc/glue/ProtocolTypes.ipdlh
new file mode 100644
index 000000000..b1d531673
--- /dev/null
+++ b/ipc/glue/ProtocolTypes.ipdlh
@@ -0,0 +1,21 @@
+/* -*- 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/. */
+
+using struct nsID
+ from "nsID.h";
+
+namespace mozilla {
+namespace ipc {
+
+struct ProtocolFdMapping
+{
+ uint32_t protocolId;
+ FileDescriptor fd;
+};
+
+}
+}
+
diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp
new file mode 100644
index 000000000..1a022048f
--- /dev/null
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -0,0 +1,720 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/process_util.h"
+#include "base/task.h"
+
+#ifdef OS_POSIX
+#include <errno.h>
+#endif
+
+#include "mozilla/ipc/ProtocolUtils.h"
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/Transport.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/Unused.h"
+#include "nsPrintfCString.h"
+
+#if defined(MOZ_SANDBOX) && defined(XP_WIN)
+#define TARGET_SANDBOX_EXPORTS
+#include "mozilla/sandboxTarget.h"
+#endif
+
+#if defined(MOZ_CRASHREPORTER) && defined(XP_WIN)
+#include "aclapi.h"
+#include "sddl.h"
+
+#include "mozilla/TypeTraits.h"
+#endif
+
+#include "nsAutoPtr.h"
+
+using namespace IPC;
+
+using base::GetCurrentProcId;
+using base::ProcessHandle;
+using base::ProcessId;
+
+namespace mozilla {
+
+#if defined(MOZ_CRASHREPORTER) && defined(XP_WIN)
+// Generate RAII classes for LPTSTR and PSECURITY_DESCRIPTOR.
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedLPTStr, \
+ RemovePointer<LPTSTR>::Type, \
+ ::LocalFree)
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPSecurityDescriptor, \
+ RemovePointer<PSECURITY_DESCRIPTOR>::Type, \
+ ::LocalFree)
+#endif
+
+namespace ipc {
+
+class ChannelOpened : public IPC::Message
+{
+public:
+ ChannelOpened(TransportDescriptor aDescriptor,
+ ProcessId aOtherProcess,
+ ProtocolId aProtocol,
+ NestedLevel aNestedLevel = NOT_NESTED)
+ : IPC::Message(MSG_ROUTING_CONTROL, // these only go to top-level actors
+ CHANNEL_OPENED_MESSAGE_TYPE,
+ aNestedLevel)
+ {
+ IPC::WriteParam(this, aDescriptor);
+ IPC::WriteParam(this, aOtherProcess);
+ IPC::WriteParam(this, static_cast<uint32_t>(aProtocol));
+ }
+
+ static bool Read(const IPC::Message& aMsg,
+ TransportDescriptor* aDescriptor,
+ ProcessId* aOtherProcess,
+ ProtocolId* aProtocol)
+ {
+ PickleIterator iter(aMsg);
+ if (!IPC::ReadParam(&aMsg, &iter, aDescriptor) ||
+ !IPC::ReadParam(&aMsg, &iter, aOtherProcess) ||
+ !IPC::ReadParam(&aMsg, &iter, reinterpret_cast<uint32_t*>(aProtocol))) {
+ return false;
+ }
+ aMsg.EndRead(iter);
+ return true;
+ }
+};
+
+nsresult
+Bridge(const PrivateIPDLInterface&,
+ MessageChannel* aParentChannel, ProcessId aParentPid,
+ MessageChannel* aChildChannel, ProcessId aChildPid,
+ ProtocolId aProtocol, ProtocolId aChildProtocol)
+{
+ if (!aParentPid || !aChildPid) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ TransportDescriptor parentSide, childSide;
+ nsresult rv;
+ if (NS_FAILED(rv = CreateTransport(aParentPid, &parentSide, &childSide))) {
+ return rv;
+ }
+
+ if (!aParentChannel->Send(new ChannelOpened(parentSide,
+ aChildPid,
+ aProtocol,
+ IPC::Message::NESTED_INSIDE_CPOW))) {
+ CloseDescriptor(parentSide);
+ CloseDescriptor(childSide);
+ return NS_ERROR_BRIDGE_OPEN_PARENT;
+ }
+
+ if (!aChildChannel->Send(new ChannelOpened(childSide,
+ aParentPid,
+ aChildProtocol,
+ IPC::Message::NESTED_INSIDE_CPOW))) {
+ CloseDescriptor(parentSide);
+ CloseDescriptor(childSide);
+ return NS_ERROR_BRIDGE_OPEN_CHILD;
+ }
+
+ return NS_OK;
+}
+
+bool
+Open(const PrivateIPDLInterface&,
+ MessageChannel* aOpenerChannel, ProcessId aOtherProcessId,
+ Transport::Mode aOpenerMode,
+ ProtocolId aProtocol, ProtocolId aChildProtocol)
+{
+ bool isParent = (Transport::MODE_SERVER == aOpenerMode);
+ ProcessId thisPid = GetCurrentProcId();
+ ProcessId parentId = isParent ? thisPid : aOtherProcessId;
+ ProcessId childId = !isParent ? thisPid : aOtherProcessId;
+ if (!parentId || !childId) {
+ return false;
+ }
+
+ TransportDescriptor parentSide, childSide;
+ if (NS_FAILED(CreateTransport(parentId, &parentSide, &childSide))) {
+ return false;
+ }
+
+ Message* parentMsg = new ChannelOpened(parentSide, childId, aProtocol);
+ Message* childMsg = new ChannelOpened(childSide, parentId, aChildProtocol);
+ nsAutoPtr<Message> messageForUs(isParent ? parentMsg : childMsg);
+ nsAutoPtr<Message> messageForOtherSide(!isParent ? parentMsg : childMsg);
+ if (!aOpenerChannel->Echo(messageForUs.forget()) ||
+ !aOpenerChannel->Send(messageForOtherSide.forget())) {
+ CloseDescriptor(parentSide);
+ CloseDescriptor(childSide);
+ return false;
+ }
+ return true;
+}
+
+bool
+UnpackChannelOpened(const PrivateIPDLInterface&,
+ const Message& aMsg,
+ TransportDescriptor* aTransport,
+ ProcessId* aOtherProcess,
+ ProtocolId* aProtocol)
+{
+ return ChannelOpened::Read(aMsg, aTransport, aOtherProcess, aProtocol);
+}
+
+#if defined(XP_WIN)
+bool DuplicateHandle(HANDLE aSourceHandle,
+ DWORD aTargetProcessId,
+ HANDLE* aTargetHandle,
+ DWORD aDesiredAccess,
+ DWORD aOptions) {
+ // If our process is the target just duplicate the handle.
+ if (aTargetProcessId == base::GetCurrentProcId()) {
+ return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
+ ::GetCurrentProcess(), aTargetHandle,
+ aDesiredAccess, false, aOptions);
+
+ }
+
+#if defined(MOZ_SANDBOX)
+ // Try the broker next (will fail if not sandboxed).
+ if (SandboxTarget::Instance()->BrokerDuplicateHandle(aSourceHandle,
+ aTargetProcessId,
+ aTargetHandle,
+ aDesiredAccess,
+ aOptions)) {
+ return true;
+ }
+#endif
+
+ // Finally, see if we already have access to the process.
+ ScopedProcessHandle targetProcess(OpenProcess(PROCESS_DUP_HANDLE,
+ FALSE,
+ aTargetProcessId));
+ if (!targetProcess) {
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("IPCTransportFailureReason"),
+ NS_LITERAL_CSTRING("Failed to open target process."));
+#endif
+ return false;
+ }
+
+ return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
+ targetProcess, aTargetHandle,
+ aDesiredAccess, FALSE, aOptions);
+}
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+void
+AnnotateSystemError()
+{
+ int64_t error = 0;
+#if defined(XP_WIN)
+ error = ::GetLastError();
+#elif defined(OS_POSIX)
+ error = errno;
+#endif
+ if (error) {
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("IPCSystemError"),
+ nsPrintfCString("%lld", error));
+ }
+}
+#endif
+
+#if defined(MOZ_CRASHREPORTER) && defined(XP_MACOSX)
+void
+AnnotateCrashReportWithErrno(const char* tag, int error)
+{
+ CrashReporter::AnnotateCrashReport(
+ nsCString(tag),
+ nsPrintfCString("%d", error));
+}
+#endif
+
+void
+LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid,
+ const char* aContextDescription,
+ uint32_t aMessageId,
+ MessageDirection aDirection)
+{
+ nsPrintfCString logMessage("[time: %" PRId64 "][%d%s%d] [%s] %s %s\n",
+ PR_Now(), base::GetCurrentProcId(),
+ aDirection == MessageDirection::eReceiving ? "<-" : "->",
+ aOtherPid, aTopLevelProtocol,
+ aContextDescription,
+ StringFromIPCMessageType(aMessageId));
+#ifdef ANDROID
+ __android_log_write(ANDROID_LOG_INFO, "GeckoIPC", logMessage.get());
+#endif
+ fputs(logMessage.get(), stderr);
+}
+
+void
+ProtocolErrorBreakpoint(const char* aMsg)
+{
+ // Bugs that generate these error messages can be tough to
+ // reproduce. Log always in the hope that someone finds the error
+ // message.
+ printf_stderr("IPDL protocol error: %s\n", aMsg);
+}
+
+void
+FatalError(const char* aProtocolName, const char* aMsg, bool aIsParent)
+{
+ ProtocolErrorBreakpoint(aMsg);
+
+ nsAutoCString formattedMessage("IPDL error [");
+ formattedMessage.AppendASCII(aProtocolName);
+ formattedMessage.AppendLiteral("]: \"");
+ formattedMessage.AppendASCII(aMsg);
+ if (aIsParent) {
+#ifdef MOZ_CRASHREPORTER
+ // We're going to crash the parent process because at this time
+ // there's no other really nice way of getting a minidump out of
+ // this process if we're off the main thread.
+ formattedMessage.AppendLiteral("\". Intentionally crashing.");
+ NS_ERROR(formattedMessage.get());
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorProtocol"),
+ nsDependentCString(aProtocolName));
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorMsg"),
+ nsDependentCString(aMsg));
+ AnnotateSystemError();
+#endif
+ MOZ_CRASH("IPC FatalError in the parent process!");
+ } else {
+ formattedMessage.AppendLiteral("\". abort()ing as a result.");
+ NS_RUNTIMEABORT(formattedMessage.get());
+ }
+}
+
+void
+LogicError(const char* aMsg)
+{
+ NS_RUNTIMEABORT(aMsg);
+}
+
+void
+ActorIdReadError(const char* aActorDescription)
+{
+ nsPrintfCString message("Error deserializing id for %s", aActorDescription);
+ NS_RUNTIMEABORT(message.get());
+}
+
+void
+BadActorIdError(const char* aActorDescription)
+{
+ nsPrintfCString message("bad id for %s", aActorDescription);
+ ProtocolErrorBreakpoint(message.get());
+}
+
+void
+ActorLookupError(const char* aActorDescription)
+{
+ nsPrintfCString message("could not lookup id for %s", aActorDescription);
+ ProtocolErrorBreakpoint(message.get());
+}
+
+void
+MismatchedActorTypeError(const char* aActorDescription)
+{
+ nsPrintfCString message("actor that should be of type %s has different type",
+ aActorDescription);
+ ProtocolErrorBreakpoint(message.get());
+}
+
+void
+UnionTypeReadError(const char* aUnionName)
+{
+ nsPrintfCString message("error deserializing type of union %s", aUnionName);
+ NS_RUNTIMEABORT(message.get());
+}
+
+void ArrayLengthReadError(const char* aElementName)
+{
+ nsPrintfCString message("error deserializing length of %s[]", aElementName);
+ NS_RUNTIMEABORT(message.get());
+}
+
+void
+TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
+ nsTArray<void*>& aArray)
+{
+ uint32_t i = 0;
+ void** elements = aArray.AppendElements(aTable.Count());
+ for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+ elements[i] = iter.Get()->GetKey();
+ ++i;
+ }
+}
+
+Maybe<IProtocol*>
+IProtocol::ReadActor(const IPC::Message* aMessage, PickleIterator* aIter, bool aNullable,
+ const char* aActorDescription, int32_t aProtocolTypeId)
+{
+ int32_t id;
+ if (!IPC::ReadParam(aMessage, aIter, &id)) {
+ ActorIdReadError(aActorDescription);
+ return Nothing();
+ }
+
+ if (id == 1 || (id == 0 && !aNullable)) {
+ BadActorIdError(aActorDescription);
+ return Nothing();
+ }
+
+ if (id == 0) {
+ return Some(static_cast<IProtocol*>(nullptr));
+ }
+
+ IProtocol* listener = this->Lookup(id);
+ if (!listener) {
+ ActorLookupError(aActorDescription);
+ return Nothing();
+ }
+
+ if (listener->GetProtocolTypeId() != aProtocolTypeId) {
+ MismatchedActorTypeError(aActorDescription);
+ return Nothing();
+ }
+
+ return Some(listener);
+}
+
+int32_t
+IProtocol::Register(IProtocol* aRouted)
+{
+ return Manager()->Register(aRouted);
+}
+
+int32_t
+IProtocol::RegisterID(IProtocol* aRouted, int32_t aId)
+{
+ return Manager()->RegisterID(aRouted, aId);
+}
+
+IProtocol*
+IProtocol::Lookup(int32_t aId)
+{
+ return Manager()->Lookup(aId);
+}
+
+void
+IProtocol::Unregister(int32_t aId)
+{
+ Manager()->Unregister(aId);
+}
+
+Shmem::SharedMemory*
+IProtocol::CreateSharedMemory(size_t aSize,
+ SharedMemory::SharedMemoryType aType,
+ bool aUnsafe,
+ int32_t* aId)
+{
+ return Manager()->CreateSharedMemory(aSize, aType, aUnsafe, aId);
+}
+
+Shmem::SharedMemory*
+IProtocol::LookupSharedMemory(int32_t aId)
+{
+ return Manager()->LookupSharedMemory(aId);
+}
+
+bool
+IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment)
+{
+ return Manager()->IsTrackingSharedMemory(aSegment);
+}
+
+bool
+IProtocol::DestroySharedMemory(Shmem& aShmem)
+{
+ return Manager()->DestroySharedMemory(aShmem);
+}
+
+ProcessId
+IProtocol::OtherPid() const
+{
+ return Manager()->OtherPid();
+}
+
+void
+IProtocol::FatalError(const char* const aErrorMsg) const
+{
+ HandleFatalError(ProtocolName(), aErrorMsg);
+}
+
+void
+IProtocol::HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const
+{
+ if (IProtocol* manager = Manager()) {
+ manager->HandleFatalError(aProtocolName, aErrorMsg);
+ return;
+ }
+
+ mozilla::ipc::FatalError(aProtocolName, aErrorMsg, mSide == ParentSide);
+}
+
+bool
+IProtocol::AllocShmem(size_t aSize,
+ Shmem::SharedMemory::SharedMemoryType aType,
+ Shmem* aOutMem)
+{
+ Shmem::id_t id;
+ Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, false, &id));
+ if (!rawmem) {
+ return false;
+ }
+
+ *aOutMem = Shmem(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), rawmem, id);
+ return true;
+}
+
+bool
+IProtocol::AllocUnsafeShmem(size_t aSize,
+ Shmem::SharedMemory::SharedMemoryType aType,
+ Shmem* aOutMem)
+{
+ Shmem::id_t id;
+ Shmem::SharedMemory* rawmem(CreateSharedMemory(aSize, aType, true, &id));
+ if (!rawmem) {
+ return false;
+ }
+
+ *aOutMem = Shmem(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), rawmem, id);
+ return true;
+}
+
+bool
+IProtocol::DeallocShmem(Shmem& aMem)
+{
+ bool ok = DestroySharedMemory(aMem);
+#ifdef DEBUG
+ if (!ok) {
+ if (mSide == ChildSide) {
+ FatalError("bad Shmem");
+ } else {
+ NS_WARNING("bad Shmem");
+ }
+ return false;
+ }
+#endif // DEBUG
+ aMem.forget(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
+ return ok;
+}
+
+IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId, Side aSide)
+ : IProtocol(aSide),
+ mProtocolId(aProtoId),
+ mOtherPid(mozilla::ipc::kInvalidProcessId),
+ mLastRouteId(aSide == ParentSide ? 1 : 0),
+ mLastShmemId(aSide == ParentSide ? 1 : 0)
+{
+}
+
+IToplevelProtocol::~IToplevelProtocol()
+{
+ if (mTrans) {
+ RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTrans.release());
+ XRE_GetIOMessageLoop()->PostTask(task.forget());
+ }
+}
+
+base::ProcessId
+IToplevelProtocol::OtherPid() const
+{
+ return mOtherPid;
+}
+
+void
+IToplevelProtocol::SetOtherProcessId(base::ProcessId aOtherPid)
+{
+ mOtherPid = aOtherPid;
+}
+
+bool
+IToplevelProtocol::TakeMinidump(nsIFile** aDump, uint32_t* aSequence)
+{
+ MOZ_RELEASE_ASSERT(GetSide() == ParentSide);
+#ifdef MOZ_CRASHREPORTER
+ return XRE_TakeMinidumpForChild(OtherPid(), aDump, aSequence);
+#else
+ return false;
+#endif
+}
+
+bool
+IToplevelProtocol::Open(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid,
+ MessageLoop* aThread,
+ mozilla::ipc::Side aSide)
+{
+ SetOtherProcessId(aOtherPid);
+ return GetIPCChannel()->Open(aTransport, aThread, aSide);
+}
+
+bool
+IToplevelProtocol::Open(MessageChannel* aChannel,
+ MessageLoop* aMessageLoop,
+ mozilla::ipc::Side aSide)
+{
+ SetOtherProcessId(base::GetCurrentProcId());
+ return GetIPCChannel()->Open(aChannel, aMessageLoop, aSide);
+}
+
+void
+IToplevelProtocol::Close()
+{
+ GetIPCChannel()->Close();
+}
+
+void
+IToplevelProtocol::SetReplyTimeoutMs(int32_t aTimeoutMs)
+{
+ GetIPCChannel()->SetReplyTimeoutMs(aTimeoutMs);
+}
+
+bool
+IToplevelProtocol::IsOnCxxStack() const
+{
+ return GetIPCChannel()->IsOnCxxStack();
+}
+
+int32_t
+IToplevelProtocol::Register(IProtocol* aRouted)
+{
+ int32_t id = GetSide() == ParentSide ? ++mLastRouteId : --mLastRouteId;
+ mActorMap.AddWithID(aRouted, id);
+ return id;
+}
+
+int32_t
+IToplevelProtocol::RegisterID(IProtocol* aRouted,
+ int32_t aId)
+{
+ mActorMap.AddWithID(aRouted, aId);
+ return aId;
+}
+
+IProtocol*
+IToplevelProtocol::Lookup(int32_t aId)
+{
+ return mActorMap.Lookup(aId);
+}
+
+void
+IToplevelProtocol::Unregister(int32_t aId)
+{
+ return mActorMap.Remove(aId);
+}
+
+Shmem::SharedMemory*
+IToplevelProtocol::CreateSharedMemory(size_t aSize,
+ Shmem::SharedMemory::SharedMemoryType aType,
+ bool aUnsafe,
+ Shmem::id_t* aId)
+{
+ RefPtr<Shmem::SharedMemory> segment(
+ Shmem::Alloc(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), aSize, aType, aUnsafe));
+ if (!segment) {
+ return nullptr;
+ }
+ int32_t id = GetSide() == ParentSide ? ++mLastShmemId : --mLastShmemId;
+ Shmem shmem(
+ Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(),
+ segment.get(),
+ id);
+ Message* descriptor = shmem.ShareTo(
+ Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), OtherPid(), MSG_ROUTING_CONTROL);
+ if (!descriptor) {
+ return nullptr;
+ }
+ Unused << GetIPCChannel()->Send(descriptor);
+
+ *aId = shmem.Id(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
+ Shmem::SharedMemory* rawSegment = segment.get();
+ mShmemMap.AddWithID(segment.forget().take(), *aId);
+ return rawSegment;
+}
+
+Shmem::SharedMemory*
+IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId)
+{
+ return mShmemMap.Lookup(aId);
+}
+
+bool
+IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment)
+{
+ return mShmemMap.HasData(segment);
+}
+
+bool
+IToplevelProtocol::DestroySharedMemory(Shmem& shmem)
+{
+ Shmem::id_t aId = shmem.Id(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
+ Shmem::SharedMemory* segment = LookupSharedMemory(aId);
+ if (!segment) {
+ return false;
+ }
+
+ Message* descriptor = shmem.UnshareFrom(
+ Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), OtherPid(), MSG_ROUTING_CONTROL);
+
+ mShmemMap.Remove(aId);
+ Shmem::Dealloc(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), segment);
+
+ if (!GetIPCChannel()->CanSend()) {
+ delete descriptor;
+ return true;
+ }
+
+ return descriptor && GetIPCChannel()->Send(descriptor);
+}
+
+void
+IToplevelProtocol::DeallocShmems()
+{
+ for (IDMap<SharedMemory>::const_iterator cit = mShmemMap.begin(); cit != mShmemMap.end(); ++cit) {
+ Shmem::Dealloc(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), cit->second);
+ }
+ mShmemMap.Clear();
+}
+
+bool
+IToplevelProtocol::ShmemCreated(const Message& aMsg)
+{
+ Shmem::id_t id;
+ RefPtr<Shmem::SharedMemory> rawmem(Shmem::OpenExisting(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), aMsg, &id, true));
+ if (!rawmem) {
+ return false;
+ }
+ mShmemMap.AddWithID(rawmem.forget().take(), id);
+ return true;
+}
+
+bool
+IToplevelProtocol::ShmemDestroyed(const Message& aMsg)
+{
+ Shmem::id_t id;
+ PickleIterator iter = PickleIterator(aMsg);
+ if (!IPC::ReadParam(&aMsg, &iter, &id)) {
+ return false;
+ }
+ aMsg.EndRead(iter);
+
+ Shmem::SharedMemory* rawmem = LookupSharedMemory(id);
+ if (rawmem) {
+ mShmemMap.Remove(id);
+ Shmem::Dealloc(Shmem::IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead(), rawmem);
+ }
+ return true;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h
new file mode 100644
index 000000000..9184aae54
--- /dev/null
+++ b/ipc/glue/ProtocolUtils.h
@@ -0,0 +1,783 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 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/. */
+
+#ifndef mozilla_ipc_ProtocolUtils_h
+#define mozilla_ipc_ProtocolUtils_h 1
+
+#include "base/id_map.h"
+#include "base/process.h"
+#include "base/process_util.h"
+#include "chrome/common/ipc_message_utils.h"
+
+#include "prenv.h"
+
+#include "IPCMessageStart.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/ipc/Transport.h"
+#include "mozilla/ipc/MessageLink.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/UniquePtr.h"
+#include "MainThreadUtils.h"
+
+#if defined(ANDROID) && defined(DEBUG)
+#include <android/log.h>
+#endif
+
+template<typename T> class nsTHashtable;
+template<typename T> class nsPtrHashKey;
+
+// WARNING: this takes into account the private, special-message-type
+// enum in ipc_channel.h. They need to be kept in sync.
+namespace {
+// XXX the max message ID is actually kuint32max now ... when this
+// changed, the assumptions of the special message IDs changed in that
+// they're not carving out messages from likely-unallocated space, but
+// rather carving out messages from the end of space allocated to
+// protocol 0. Oops! We can get away with this until protocol 0
+// starts approaching its 65,536th message.
+enum {
+ CHANNEL_OPENED_MESSAGE_TYPE = kuint16max - 6,
+ SHMEM_DESTROYED_MESSAGE_TYPE = kuint16max - 5,
+ SHMEM_CREATED_MESSAGE_TYPE = kuint16max - 4,
+ GOODBYE_MESSAGE_TYPE = kuint16max - 3,
+ CANCEL_MESSAGE_TYPE = kuint16max - 2,
+
+ // kuint16max - 1 is used by ipc_channel.h.
+};
+
+} // namespace
+
+namespace mozilla {
+namespace dom {
+class ContentParent;
+} // namespace dom
+
+namespace net {
+class NeckoParent;
+} // namespace net
+
+namespace ipc {
+
+class MessageChannel;
+
+#ifdef XP_WIN
+const base::ProcessHandle kInvalidProcessHandle = INVALID_HANDLE_VALUE;
+
+// In theory, on Windows, this is a valid process ID, but in practice they are
+// currently divisible by four. Process IDs share the kernel handle allocation
+// code and they are guaranteed to be divisible by four.
+// As this could change for process IDs we shouldn't generally rely on this
+// property, however even if that were to change, it seems safe to rely on this
+// particular value never being used.
+const base::ProcessId kInvalidProcessId = kuint32max;
+#else
+const base::ProcessHandle kInvalidProcessHandle = -1;
+const base::ProcessId kInvalidProcessId = -1;
+#endif
+
+// Scoped base::ProcessHandle to ensure base::CloseProcessHandle is called.
+struct ScopedProcessHandleTraits
+{
+ typedef base::ProcessHandle type;
+
+ static type empty()
+ {
+ return kInvalidProcessHandle;
+ }
+
+ static void release(type aProcessHandle)
+ {
+ if (aProcessHandle && aProcessHandle != kInvalidProcessHandle) {
+ base::CloseProcessHandle(aProcessHandle);
+ }
+ }
+};
+typedef mozilla::Scoped<ScopedProcessHandleTraits> ScopedProcessHandle;
+
+class ProtocolFdMapping;
+class ProtocolCloneContext;
+
+// Used to pass references to protocol actors across the wire.
+// Actors created on the parent-side have a positive ID, and actors
+// allocated on the child side have a negative ID.
+struct ActorHandle
+{
+ int mId;
+};
+
+// Used internally to represent a "trigger" that might cause a state
+// transition. Triggers are normalized across parent+child to Send
+// and Recv (instead of child-in, child-out, parent-in, parent-out) so
+// that they can share the same state machine implementation. To
+// further normalize, |Send| is used for 'call', |Recv| for 'answer'.
+struct Trigger
+{
+ enum Action { Send, Recv };
+
+ Trigger(Action action, int32_t msg) :
+ mAction(action),
+ mMessage(msg)
+ {
+ MOZ_ASSERT(0 <= msg && msg < INT32_MAX);
+ }
+
+ uint32_t mAction : 1;
+ uint32_t mMessage : 31;
+};
+
+// What happens if Interrupt calls race?
+enum RacyInterruptPolicy {
+ RIPError,
+ RIPChildWins,
+ RIPParentWins
+};
+
+class IProtocol : public HasResultCodes
+{
+public:
+ enum ActorDestroyReason {
+ FailedConstructor,
+ Deletion,
+ AncestorDeletion,
+ NormalShutdown,
+ AbnormalShutdown
+ };
+
+ typedef base::ProcessId ProcessId;
+ typedef IPC::Message Message;
+ typedef IPC::MessageInfo MessageInfo;
+
+ IProtocol(Side aSide) : mId(0), mSide(aSide), mManager(nullptr), mChannel(nullptr) {}
+
+ virtual int32_t Register(IProtocol*);
+ virtual int32_t RegisterID(IProtocol*, int32_t);
+ virtual IProtocol* Lookup(int32_t);
+ virtual void Unregister(int32_t);
+ virtual void RemoveManagee(int32_t, IProtocol*) = 0;
+
+ virtual Shmem::SharedMemory* CreateSharedMemory(
+ size_t, SharedMemory::SharedMemoryType, bool, int32_t*);
+ virtual Shmem::SharedMemory* LookupSharedMemory(int32_t);
+ virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*);
+ virtual bool DestroySharedMemory(Shmem&);
+
+ // XXX odd ducks, acknowledged
+ virtual ProcessId OtherPid() const;
+ Side GetSide() const { return mSide; }
+
+ virtual const char* ProtocolName() const = 0;
+ void FatalError(const char* const aErrorMsg) const;
+ virtual void HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const;
+
+ Maybe<IProtocol*> ReadActor(const IPC::Message* aMessage, PickleIterator* aIter, bool aNullable,
+ const char* aActorDescription, int32_t aProtocolTypeId);
+
+ virtual Result OnMessageReceived(const Message& aMessage) = 0;
+ virtual Result OnMessageReceived(const Message& aMessage, Message *& aReply) = 0;
+ virtual Result OnCallReceived(const Message& aMessage, Message *& aReply) = 0;
+
+ virtual int32_t GetProtocolTypeId() = 0;
+
+ int32_t Id() const { return mId; }
+ IProtocol* Manager() const { return mManager; }
+ virtual const MessageChannel* GetIPCChannel() const { return mChannel; }
+ virtual MessageChannel* GetIPCChannel() { return mChannel; }
+
+ bool AllocShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aOutMem);
+ bool AllocUnsafeShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aOutMem);
+ bool DeallocShmem(Shmem& aMem);
+
+protected:
+ void SetId(int32_t aId) { mId = aId; }
+ void SetManager(IProtocol* aManager) { mManager = aManager; }
+ void SetIPCChannel(MessageChannel* aChannel) { mChannel = aChannel; }
+
+private:
+ int32_t mId;
+ Side mSide;
+ IProtocol* mManager;
+ MessageChannel* mChannel;
+};
+
+typedef IPCMessageStart ProtocolId;
+
+template<class PFooSide>
+class Endpoint;
+
+/**
+ * All top-level protocols should inherit this class.
+ *
+ * IToplevelProtocol tracks all top-level protocol actors created from
+ * this protocol actor.
+ */
+class IToplevelProtocol : public IProtocol
+{
+ template<class PFooSide> friend class Endpoint;
+
+protected:
+ explicit IToplevelProtocol(ProtocolId aProtoId, Side aSide);
+ ~IToplevelProtocol();
+
+public:
+ void SetTransport(UniquePtr<Transport> aTrans)
+ {
+ mTrans = Move(aTrans);
+ }
+
+ Transport* GetTransport() const { return mTrans.get(); }
+
+ ProtocolId GetProtocolId() const { return mProtocolId; }
+
+ base::ProcessId OtherPid() const;
+ void SetOtherProcessId(base::ProcessId aOtherPid);
+
+ bool TakeMinidump(nsIFile** aDump, uint32_t* aSequence);
+
+ virtual void OnChannelClose() = 0;
+ virtual void OnChannelError() = 0;
+ virtual void ProcessingError(Result aError, const char* aMsgName) {}
+ virtual void OnChannelConnected(int32_t peer_pid) {}
+
+ bool Open(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid,
+ MessageLoop* aThread = nullptr,
+ mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
+
+ bool Open(MessageChannel* aChannel,
+ MessageLoop* aMessageLoop,
+ mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
+
+ void Close();
+
+ void SetReplyTimeoutMs(int32_t aTimeoutMs);
+
+ virtual int32_t Register(IProtocol*);
+ virtual int32_t RegisterID(IProtocol*, int32_t);
+ virtual IProtocol* Lookup(int32_t);
+ virtual void Unregister(int32_t);
+
+ virtual Shmem::SharedMemory* CreateSharedMemory(
+ size_t, SharedMemory::SharedMemoryType, bool, int32_t*);
+ virtual Shmem::SharedMemory* LookupSharedMemory(int32_t);
+ virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*);
+ virtual bool DestroySharedMemory(Shmem&);
+
+ void DeallocShmems();
+
+ bool ShmemCreated(const Message& aMsg);
+ bool ShmemDestroyed(const Message& aMsg);
+
+ virtual bool ShouldContinueFromReplyTimeout() {
+ return false;
+ }
+
+ // WARNING: This function is called with the MessageChannel monitor held.
+ virtual void IntentionalCrash() {
+ MOZ_CRASH("Intentional IPDL crash");
+ }
+
+ // The code here is only useful for fuzzing. It should not be used for any
+ // other purpose.
+#ifdef DEBUG
+ // Returns true if we should simulate a timeout.
+ // WARNING: This is a testing-only function that is called with the
+ // MessageChannel monitor held. Don't do anything fancy here or we could
+ // deadlock.
+ virtual bool ArtificialTimeout() {
+ return false;
+ }
+
+ // Returns true if we want to cause the worker thread to sleep with the
+ // monitor unlocked.
+ virtual bool NeedArtificialSleep() {
+ return false;
+ }
+
+ // This function should be implemented to sleep for some amount of time on
+ // the worker thread. Will only be called if NeedArtificialSleep() returns
+ // true.
+ virtual void ArtificialSleep() {}
+#else
+ bool ArtificialTimeout() { return false; }
+ bool NeedArtificialSleep() { return false; }
+ void ArtificialSleep() {}
+#endif
+
+ virtual void EnteredCxxStack() {}
+ virtual void ExitedCxxStack() {}
+ virtual void EnteredCall() {}
+ virtual void ExitedCall() {}
+
+ bool IsOnCxxStack() const;
+
+ virtual RacyInterruptPolicy MediateInterruptRace(const MessageInfo& parent,
+ const MessageInfo& child)
+ {
+ return RIPChildWins;
+ }
+
+ /**
+ * Return true if windows messages can be handled while waiting for a reply
+ * to a sync IPDL message.
+ */
+ virtual bool HandleWindowsMessages(const Message& aMsg) const { return true; }
+
+ virtual void OnEnteredSyncSend() {
+ }
+ virtual void OnExitedSyncSend() {
+ }
+
+ virtual void ProcessRemoteNativeEventsInInterruptCall() {
+ }
+
+private:
+ ProtocolId mProtocolId;
+ UniquePtr<Transport> mTrans;
+ base::ProcessId mOtherPid;
+ IDMap<IProtocol> mActorMap;
+ int32_t mLastRouteId;
+ IDMap<Shmem::SharedMemory> mShmemMap;
+ Shmem::id_t mLastShmemId;
+};
+
+class IShmemAllocator
+{
+public:
+ virtual bool AllocShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual bool AllocUnsafeShmem(size_t aSize,
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
+ mozilla::ipc::Shmem* aShmem) = 0;
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0;
+};
+
+#define FORWARD_SHMEM_ALLOCATOR_TO(aImplClass) \
+ virtual bool AllocShmem(size_t aSize, \
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
+ mozilla::ipc::Shmem* aShmem) override \
+ { return aImplClass::AllocShmem(aSize, aShmType, aShmem); } \
+ virtual bool AllocUnsafeShmem(size_t aSize, \
+ mozilla::ipc::SharedMemory::SharedMemoryType aShmType, \
+ mozilla::ipc::Shmem* aShmem) override \
+ { return aImplClass::AllocUnsafeShmem(aSize, aShmType, aShmem); } \
+ virtual bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override \
+ { return aImplClass::DeallocShmem(aShmem); }
+
+inline bool
+LoggingEnabled()
+{
+#if defined(DEBUG)
+ return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
+#else
+ return false;
+#endif
+}
+
+inline bool
+LoggingEnabledFor(const char *aTopLevelProtocol)
+{
+#if defined(DEBUG)
+ const char *filter = PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
+ if (!filter) {
+ return false;
+ }
+ return strcmp(filter, "1") == 0 || strcmp(filter, aTopLevelProtocol) == 0;
+#else
+ return false;
+#endif
+}
+
+enum class MessageDirection {
+ eSending,
+ eReceiving,
+};
+
+MOZ_NEVER_INLINE void
+LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid,
+ const char* aContextDescription,
+ uint32_t aMessageId,
+ MessageDirection aDirection);
+
+MOZ_NEVER_INLINE void
+ProtocolErrorBreakpoint(const char* aMsg);
+
+// The code generator calls this function for errors which come from the
+// methods of protocols. Doing this saves codesize by making the error
+// cases significantly smaller.
+MOZ_NEVER_INLINE void
+FatalError(const char* aProtocolName, const char* aMsg, bool aIsParent);
+
+// The code generator calls this function for errors which are not
+// protocol-specific: errors in generated struct methods or errors in
+// transition functions, for instance. Doing this saves codesize by
+// by making the error cases significantly smaller.
+MOZ_NEVER_INLINE void
+LogicError(const char* aMsg);
+
+MOZ_NEVER_INLINE void
+ActorIdReadError(const char* aActorDescription);
+
+MOZ_NEVER_INLINE void
+BadActorIdError(const char* aActorDescription);
+
+MOZ_NEVER_INLINE void
+ActorLookupError(const char* aActorDescription);
+
+MOZ_NEVER_INLINE void
+MismatchedActorTypeError(const char* aActorDescription);
+
+MOZ_NEVER_INLINE void
+UnionTypeReadError(const char* aUnionName);
+
+MOZ_NEVER_INLINE void
+ArrayLengthReadError(const char* aElementName);
+
+struct PrivateIPDLInterface {};
+
+nsresult
+Bridge(const PrivateIPDLInterface&,
+ MessageChannel*, base::ProcessId, MessageChannel*, base::ProcessId,
+ ProtocolId, ProtocolId);
+
+bool
+Open(const PrivateIPDLInterface&,
+ MessageChannel*, base::ProcessId, Transport::Mode,
+ ProtocolId, ProtocolId);
+
+bool
+UnpackChannelOpened(const PrivateIPDLInterface&,
+ const IPC::Message&,
+ TransportDescriptor*, base::ProcessId*, ProtocolId*);
+
+#if defined(XP_WIN)
+// This is a restricted version of Windows' DuplicateHandle() function
+// that works inside the sandbox and can send handles but not retrieve
+// them. Unlike DuplicateHandle(), it takes a process ID rather than
+// a process handle. It returns true on success, false otherwise.
+bool
+DuplicateHandle(HANDLE aSourceHandle,
+ DWORD aTargetProcessId,
+ HANDLE* aTargetHandle,
+ DWORD aDesiredAccess,
+ DWORD aOptions);
+#endif
+
+/**
+ * Annotate the crash reporter with the error code from the most recent system
+ * call. Returns the system error.
+ */
+#ifdef MOZ_CRASHREPORTER
+void AnnotateSystemError();
+#else
+#define AnnotateSystemError() do { } while (0)
+#endif
+
+/**
+ * An endpoint represents one end of a partially initialized IPDL channel. To
+ * set up a new top-level protocol:
+ *
+ * Endpoint<PFooParent> parentEp;
+ * Endpoint<PFooChild> childEp;
+ * nsresult rv;
+ * rv = PFoo::CreateEndpoints(parentPid, childPid, &parentEp, &childEp);
+ *
+ * You're required to pass in parentPid and childPid, which are the pids of the
+ * processes in which the parent and child endpoints will be used.
+ *
+ * Endpoints can be passed in IPDL messages or sent to other threads using
+ * PostTask. Once an Endpoint has arrived at its destination process and thread,
+ * you need to create the top-level actor and bind it to the endpoint:
+ *
+ * FooParent* parent = new FooParent();
+ * bool rv1 = parentEp.Bind(parent, processActor);
+ * bool rv2 = parent->SendBar(...);
+ *
+ * (See Bind below for an explanation of processActor.) Once the actor is bound
+ * to the endpoint, it can send and receive messages.
+ */
+template<class PFooSide>
+class Endpoint
+{
+public:
+ typedef base::ProcessId ProcessId;
+
+ Endpoint()
+ : mValid(false)
+ {}
+
+ Endpoint(const PrivateIPDLInterface&,
+ mozilla::ipc::Transport::Mode aMode,
+ TransportDescriptor aTransport,
+ ProcessId aMyPid,
+ ProcessId aOtherPid,
+ ProtocolId aProtocolId)
+ : mValid(true)
+ , mMode(aMode)
+ , mTransport(aTransport)
+ , mMyPid(aMyPid)
+ , mOtherPid(aOtherPid)
+ , mProtocolId(aProtocolId)
+ {}
+
+ Endpoint(Endpoint&& aOther)
+ : mValid(aOther.mValid)
+ , mMode(aOther.mMode)
+ , mTransport(aOther.mTransport)
+ , mMyPid(aOther.mMyPid)
+ , mOtherPid(aOther.mOtherPid)
+ , mProtocolId(aOther.mProtocolId)
+ {
+ aOther.mValid = false;
+ }
+
+ Endpoint& operator=(Endpoint&& aOther)
+ {
+ mValid = aOther.mValid;
+ mMode = aOther.mMode;
+ mTransport = aOther.mTransport;
+ mMyPid = aOther.mMyPid;
+ mOtherPid = aOther.mOtherPid;
+ mProtocolId = aOther.mProtocolId;
+
+ aOther.mValid = false;
+ return *this;
+ }
+
+ ~Endpoint() {
+ if (mValid) {
+ CloseDescriptor(mTransport);
+ }
+ }
+
+ ProcessId OtherPid() const {
+ return mOtherPid;
+ }
+
+ // This method binds aActor to this endpoint. After this call, the actor can
+ // be used to send and receive messages. The endpoint becomes invalid.
+ bool Bind(PFooSide* aActor)
+ {
+ MOZ_RELEASE_ASSERT(mValid);
+ MOZ_RELEASE_ASSERT(mMyPid == base::GetCurrentProcId());
+
+ UniquePtr<Transport> t = mozilla::ipc::OpenDescriptor(mTransport, mMode);
+ if (!t) {
+ return false;
+ }
+ if (!aActor->Open(t.get(), mOtherPid, XRE_GetIOMessageLoop(),
+ mMode == Transport::MODE_SERVER ? ParentSide : ChildSide)) {
+ return false;
+ }
+ mValid = false;
+ aActor->SetTransport(Move(t));
+ return true;
+ }
+
+ bool IsValid() const {
+ return mValid;
+ }
+
+private:
+ friend struct IPC::ParamTraits<Endpoint<PFooSide>>;
+
+ Endpoint(const Endpoint&) = delete;
+ Endpoint& operator=(const Endpoint&) = delete;
+
+ bool mValid;
+ mozilla::ipc::Transport::Mode mMode;
+ TransportDescriptor mTransport;
+ ProcessId mMyPid, mOtherPid;
+ ProtocolId mProtocolId;
+};
+
+#if defined(MOZ_CRASHREPORTER) && defined(XP_MACOSX)
+void AnnotateCrashReportWithErrno(const char* tag, int error);
+#else
+static inline void AnnotateCrashReportWithErrno(const char* tag, int error)
+{}
+#endif
+
+// This function is used internally to create a pair of Endpoints. See the
+// comment above Endpoint for a description of how it might be used.
+template<class PFooParent, class PFooChild>
+nsresult
+CreateEndpoints(const PrivateIPDLInterface& aPrivate,
+ base::ProcessId aParentDestPid,
+ base::ProcessId aChildDestPid,
+ ProtocolId aProtocol,
+ ProtocolId aChildProtocol,
+ Endpoint<PFooParent>* aParentEndpoint,
+ Endpoint<PFooChild>* aChildEndpoint)
+{
+ MOZ_RELEASE_ASSERT(aParentDestPid);
+ MOZ_RELEASE_ASSERT(aChildDestPid);
+
+ TransportDescriptor parentTransport, childTransport;
+ nsresult rv;
+ if (NS_FAILED(rv = CreateTransport(aParentDestPid, &parentTransport, &childTransport))) {
+ AnnotateCrashReportWithErrno("IpcCreateEndpointsNsresult", int(rv));
+ return rv;
+ }
+
+ *aParentEndpoint = Endpoint<PFooParent>(aPrivate, mozilla::ipc::Transport::MODE_SERVER,
+ parentTransport, aParentDestPid, aChildDestPid, aProtocol);
+
+ *aChildEndpoint = Endpoint<PFooChild>(aPrivate, mozilla::ipc::Transport::MODE_CLIENT,
+ childTransport, aChildDestPid, aParentDestPid, aChildProtocol);
+
+ return NS_OK;
+}
+
+void
+TableToArray(const nsTHashtable<nsPtrHashKey<void>>& aTable,
+ nsTArray<void*>& aArray);
+
+const char* StringFromIPCMessageType(uint32_t aMessageType);
+
+} // namespace ipc
+
+template<typename Protocol>
+class ManagedContainer : public nsTHashtable<nsPtrHashKey<Protocol>>
+{
+ typedef nsTHashtable<nsPtrHashKey<Protocol>> BaseClass;
+
+public:
+ // Having the core logic work on void pointers, rather than typed pointers,
+ // means that we can have one instance of this code out-of-line, rather
+ // than several hundred instances of this code out-of-lined. (Those
+ // repeated instances don't necessarily get folded together by the linker
+ // because they contain member offsets and such that differ between the
+ // functions.) We do have to pay for it with some eye-bleedingly bad casts,
+ // though.
+ void ToArray(nsTArray<Protocol*>& aArray) const {
+ ::mozilla::ipc::TableToArray(*reinterpret_cast<const nsTHashtable<nsPtrHashKey<void>>*>
+ (static_cast<const BaseClass*>(this)),
+ reinterpret_cast<nsTArray<void*>&>(aArray));
+ }
+};
+
+template<typename Protocol>
+Protocol*
+LoneManagedOrNullAsserts(const ManagedContainer<Protocol>& aManagees)
+{
+ if (aManagees.IsEmpty()) {
+ return nullptr;
+ }
+ MOZ_ASSERT(aManagees.Count() == 1);
+ return aManagees.ConstIter().Get()->GetKey();
+}
+
+// appId's are for B2G only currently, where managees.Count() == 1. This is
+// not guaranteed currently in Desktop, so for paths used for desktop,
+// don't assert there's one managee.
+template<typename Protocol>
+Protocol*
+SingleManagedOrNull(const ManagedContainer<Protocol>& aManagees)
+{
+ if (aManagees.Count() != 1) {
+ return nullptr;
+ }
+ return aManagees.ConstIter().Get()->GetKey();
+}
+
+} // namespace mozilla
+
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::ipc::ActorHandle>
+{
+ typedef mozilla::ipc::ActorHandle paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ IPC::WriteParam(aMsg, aParam.mId);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ int id;
+ if (IPC::ReadParam(aMsg, aIter, &id)) {
+ aResult->mId = id;
+ return true;
+ }
+ return false;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"(%d)", aParam.mId));
+ }
+};
+
+template<class PFooSide>
+struct ParamTraits<mozilla::ipc::Endpoint<PFooSide>>
+{
+ typedef mozilla::ipc::Endpoint<PFooSide> paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ IPC::WriteParam(aMsg, aParam.mValid);
+ if (!aParam.mValid) {
+ return;
+ }
+
+ IPC::WriteParam(aMsg, static_cast<uint32_t>(aParam.mMode));
+
+ // We duplicate the descriptor so that our own file descriptor remains
+ // valid after the write. An alternative would be to set
+ // aParam.mTransport.mValid to false, but that won't work because aParam
+ // is const.
+ mozilla::ipc::TransportDescriptor desc = mozilla::ipc::DuplicateDescriptor(aParam.mTransport);
+ IPC::WriteParam(aMsg, desc);
+
+ IPC::WriteParam(aMsg, aParam.mMyPid);
+ IPC::WriteParam(aMsg, aParam.mOtherPid);
+ IPC::WriteParam(aMsg, static_cast<uint32_t>(aParam.mProtocolId));
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ MOZ_RELEASE_ASSERT(!aResult->mValid);
+
+ if (!IPC::ReadParam(aMsg, aIter, &aResult->mValid)) {
+ return false;
+ }
+ if (!aResult->mValid) {
+ // Object is empty, but read succeeded.
+ return true;
+ }
+
+ uint32_t mode, protocolId;
+ if (!IPC::ReadParam(aMsg, aIter, &mode) ||
+ !IPC::ReadParam(aMsg, aIter, &aResult->mTransport) ||
+ !IPC::ReadParam(aMsg, aIter, &aResult->mMyPid) ||
+ !IPC::ReadParam(aMsg, aIter, &aResult->mOtherPid) ||
+ !IPC::ReadParam(aMsg, aIter, &protocolId)) {
+ return false;
+ }
+ aResult->mMode = Channel::Mode(mode);
+ aResult->mProtocolId = mozilla::ipc::ProtocolId(protocolId);
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(StringPrintf(L"Endpoint"));
+ }
+};
+
+} // namespace IPC
+
+
+#endif // mozilla_ipc_ProtocolUtils_h
diff --git a/ipc/glue/ScopedXREEmbed.cpp b/ipc/glue/ScopedXREEmbed.cpp
new file mode 100644
index 000000000..b419fdb42
--- /dev/null
+++ b/ipc/glue/ScopedXREEmbed.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "ScopedXREEmbed.h"
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+
+#include "nsIFile.h"
+
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsXULAppAPI.h"
+
+using mozilla::ipc::ScopedXREEmbed;
+
+ScopedXREEmbed::ScopedXREEmbed()
+: mShouldKillEmbedding(false)
+{
+ NS_LogInit();
+}
+
+ScopedXREEmbed::~ScopedXREEmbed()
+{
+ Stop();
+ NS_LogTerm();
+}
+
+void
+ScopedXREEmbed::SetAppDir(const nsACString& aPath)
+{
+ bool flag;
+ nsresult rv =
+ XRE_GetFileFromPath(aPath.BeginReading(), getter_AddRefs(mAppDir));
+ if (NS_FAILED(rv) ||
+ NS_FAILED(mAppDir->Exists(&flag)) || !flag) {
+ NS_WARNING("Invalid application directory passed to content process.");
+ mAppDir = nullptr;
+ }
+}
+
+void
+ScopedXREEmbed::Start()
+{
+ std::string path;
+#if defined(OS_WIN)
+ path = WideToUTF8(CommandLine::ForCurrentProcess()->program());
+#elif defined(OS_POSIX)
+ path = CommandLine::ForCurrentProcess()->argv()[0];
+#else
+# error Sorry
+#endif
+
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = XRE_GetBinaryPath(path.c_str(), getter_AddRefs(localFile));
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIFile> parent;
+ rv = localFile->GetParent(getter_AddRefs(parent));
+ if (NS_FAILED(rv))
+ return;
+
+ localFile = do_QueryInterface(parent);
+ NS_ENSURE_TRUE_VOID(localFile);
+
+#ifdef OS_MACOSX
+ if (XRE_IsContentProcess()) {
+ // We're an XPCOM-using subprocess. Walk out of
+ // [subprocess].app/Contents/MacOS to the real GRE dir.
+ rv = localFile->GetParent(getter_AddRefs(parent));
+ if (NS_FAILED(rv))
+ return;
+
+ localFile = do_QueryInterface(parent);
+ NS_ENSURE_TRUE_VOID(localFile);
+
+ rv = localFile->GetParent(getter_AddRefs(parent));
+ if (NS_FAILED(rv))
+ return;
+
+ localFile = do_QueryInterface(parent);
+ NS_ENSURE_TRUE_VOID(localFile);
+
+ rv = localFile->GetParent(getter_AddRefs(parent));
+ if (NS_FAILED(rv))
+ return;
+
+ localFile = do_QueryInterface(parent);
+ NS_ENSURE_TRUE_VOID(localFile);
+
+ rv = localFile->SetNativeLeafName(NS_LITERAL_CSTRING("Resources"));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+#endif
+
+ if (mAppDir)
+ rv = XRE_InitEmbedding2(localFile, mAppDir, nullptr);
+ else
+ rv = XRE_InitEmbedding2(localFile, localFile, nullptr);
+ if (NS_FAILED(rv))
+ return;
+
+ mShouldKillEmbedding = true;
+}
+
+void
+ScopedXREEmbed::Stop()
+{
+ if (mShouldKillEmbedding) {
+ XRE_TermEmbedding();
+ mShouldKillEmbedding = false;
+ }
+}
diff --git a/ipc/glue/ScopedXREEmbed.h b/ipc/glue/ScopedXREEmbed.h
new file mode 100644
index 000000000..60f8fb396
--- /dev/null
+++ b/ipc/glue/ScopedXREEmbed.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __IPC_GLUE_SCOPEDXREEMBED_H__
+#define __IPC_GLUE_SCOPEDXREEMBED_H__
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIFile.h"
+
+namespace mozilla {
+namespace ipc {
+
+class ScopedXREEmbed
+{
+public:
+ ScopedXREEmbed();
+ ~ScopedXREEmbed();
+
+ void Start();
+ void Stop();
+ void SetAppDir(const nsACString& aPath);
+
+private:
+ bool mShouldKillEmbedding;
+ nsCOMPtr<nsIFile> mAppDir;
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* __IPC_GLUE_SCOPEDXREEMBED_H__ */
diff --git a/ipc/glue/SendStream.h b/ipc/glue/SendStream.h
new file mode 100644
index 000000000..b9bee5dec
--- /dev/null
+++ b/ipc/glue/SendStream.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_SendStream_h
+#define mozilla_ipc_SendStream_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/ipc/PSendStreamChild.h"
+#include "mozilla/ipc/PSendStreamParent.h"
+
+class nsIInputStream;
+class nsIAsyncInputStream;
+
+namespace mozilla {
+
+namespace dom {
+class nsIContentChild;
+} // dom namespace
+
+namespace ipc {
+
+class PBackgroundChild;
+
+// The SendStream IPC actor is designed to push an nsIInputStream from child to
+// parent incrementally. This is mainly needed for streams such as nsPipe that
+// may not yet have all their data available when the stream must be sent across
+// an IPC boundary. While many streams are handled by SerializeInputStream(),
+// these streams cannot be serialized and must be sent using this actor.
+//
+// The SendStream actor only supports sending data from child to parent.
+//
+// The SendStream actor only support async, non-blocking streams because they
+// must be read inline on the main thread and Worker threads.
+//
+// In general, the creation and handling of the SendStream actor cannot be
+// abstracted away behind SerializeInputStream() because the actor must be
+// carefully managed. Specifically:
+//
+// 1) The data flow must be explicitly initiated by calling
+// SendStreamChild::Start() after the actor has been sent to the parent.
+// 2) If the actor is never sent to the parent, then the child code must
+// call SendStreamChild::StartDestroy() to avoid memory leaks.
+// 3) The SendStreamChild actor can only be used on threads that can be
+// guaranteed to stay alive as long as the actor is alive. Right now
+// this limits SendStream to the main thread and Worker threads.
+//
+// In general you should probably use the AutoIPCStreamChild RAII class
+// defined in InputStreamUtils.h instead of using SendStreamChild directly.
+class SendStreamChild : public PSendStreamChild
+{
+public:
+ // Create a SendStreamChild using a PContent IPC manager on the
+ // main thread. This can return nullptr if the provided stream is
+ // blocking.
+ static SendStreamChild*
+ Create(nsIAsyncInputStream* aInputStream, dom::nsIContentChild* aManager);
+
+ // Create a SendStreamChild using a PBackground IPC manager on the
+ // main thread or a Worker thread. This can return nullptr if the provided
+ // stream is blocking or if the Worker thread is already shutting down.
+ static SendStreamChild*
+ Create(nsIAsyncInputStream* aInputStream, PBackgroundChild* aManager);
+
+ // Start reading data from the nsIAsyncInputStream used to create the actor.
+ // This must be called after the actor is passed to the parent. If you
+ // use AutoIPCStream this is handled automatically.
+ virtual void
+ Start() = 0;
+
+ // Start cleaning up the actor. This must be called if the actor is never
+ // sent to the parent. If you use AutoIPCStream this is handled
+ // automatically.
+ virtual void
+ StartDestroy() = 0;
+
+protected:
+ virtual
+ ~SendStreamChild() = 0;
+};
+
+// On the parent side, you must simply call TakeReader() upon receiving a
+// reference to the SendStreamParent actor. You do not need to maintain a
+// reference to the actor itself.
+class SendStreamParent : public PSendStreamParent
+{
+public:
+ virtual already_AddRefed<nsIInputStream>
+ TakeReader() = 0;
+
+protected:
+ virtual
+ ~SendStreamParent() = 0;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_SendStream_h
diff --git a/ipc/glue/SendStreamAlloc.h b/ipc/glue/SendStreamAlloc.h
new file mode 100644
index 000000000..e33639ced
--- /dev/null
+++ b/ipc/glue/SendStreamAlloc.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_SendStreamAlloc_h
+#define mozilla_ipc_SendStreamAlloc_h
+
+namespace mozilla {
+namespace ipc {
+
+class PSendStreamParent;
+
+PSendStreamParent*
+AllocPSendStreamParent();
+
+} // ipc namespace
+} // mozilla namespace
+
+#endif // mozilla_ipc_SendStreamAlloc_h
diff --git a/ipc/glue/SendStreamChild.cpp b/ipc/glue/SendStreamChild.cpp
new file mode 100644
index 000000000..02e8726e8
--- /dev/null
+++ b/ipc/glue/SendStreamChild.cpp
@@ -0,0 +1,429 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/SendStream.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/nsIContentChild.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/workers/bindings/WorkerHolder.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsIAsyncInputStream.h"
+#include "nsICancelableRunnable.h"
+#include "nsIRunnable.h"
+#include "nsIThread.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+namespace ipc {
+
+using mozilla::dom::nsIContentChild;
+using mozilla::dom::workers::Canceling;
+using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
+using mozilla::dom::workers::Status;
+using mozilla::dom::workers::WorkerHolder;
+using mozilla::dom::workers::WorkerPrivate;
+
+namespace {
+
+class SendStreamChildImpl final : public SendStreamChild
+ , public WorkerHolder
+{
+public:
+ explicit SendStreamChildImpl(nsIAsyncInputStream* aStream);
+ ~SendStreamChildImpl();
+
+ void Start() override;
+ void StartDestroy() override;
+
+ bool
+ AddAsWorkerHolder(dom::workers::WorkerPrivate* aWorkerPrivate);
+
+private:
+ class Callback;
+
+ // PSendStreamChild methods
+ virtual void
+ ActorDestroy(ActorDestroyReason aReason) override;
+
+ virtual bool
+ RecvRequestClose(const nsresult& aRv) override;
+
+ // WorkerHolder methods
+ virtual bool
+ Notify(Status aStatus) override;
+
+ void DoRead();
+
+ void Wait();
+
+ void OnStreamReady(Callback* aCallback);
+
+ void OnEnd(nsresult aRv);
+
+ nsCOMPtr<nsIAsyncInputStream> mStream;
+ RefPtr<Callback> mCallback;
+ WorkerPrivate* mWorkerPrivate;
+ bool mClosed;
+
+ NS_DECL_OWNINGTHREAD
+};
+
+class SendStreamChildImpl::Callback final : public nsIInputStreamCallback
+ , public nsIRunnable
+ , public nsICancelableRunnable
+{
+public:
+ explicit Callback(SendStreamChildImpl* aActor)
+ : mActor(aActor)
+ , mOwningThread(NS_GetCurrentThread())
+ {
+ MOZ_ASSERT(mActor);
+ }
+
+ NS_IMETHOD
+ OnInputStreamReady(nsIAsyncInputStream* aStream) override
+ {
+ // any thread
+ if (mOwningThread == NS_GetCurrentThread()) {
+ return Run();
+ }
+
+ // If this fails, then it means the owning thread is a Worker that has
+ // been shutdown. Its ok to lose the event in this case because the
+ // SendStreamChild listens for this event through the WorkerHolder.
+ nsresult rv = mOwningThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch stream readable event to owning thread");
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
+ if (mActor) {
+ mActor->OnStreamReady(this);
+ }
+ return NS_OK;
+ }
+
+ nsresult
+ Cancel() override
+ {
+ // Cancel() gets called when the Worker thread is being shutdown. We have
+ // nothing to do here because SendStreamChild handles this case via
+ // the WorkerHolder.
+ return NS_OK;
+ }
+
+ void
+ ClearActor()
+ {
+ MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
+ MOZ_ASSERT(mActor);
+ mActor = nullptr;
+ }
+
+private:
+ ~Callback()
+ {
+ // called on any thread
+
+ // ClearActor() should be called before the Callback is destroyed
+ MOZ_ASSERT(!mActor);
+ }
+
+ SendStreamChildImpl* mActor;
+ nsCOMPtr<nsIThread> mOwningThread;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(SendStreamChildImpl::Callback, nsIInputStreamCallback,
+ nsIRunnable,
+ nsICancelableRunnable);
+
+SendStreamChildImpl::SendStreamChildImpl(nsIAsyncInputStream* aStream)
+ : mStream(aStream)
+ , mWorkerPrivate(nullptr)
+ , mClosed(false)
+{
+ MOZ_ASSERT(mStream);
+}
+
+SendStreamChildImpl::~SendStreamChildImpl()
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ MOZ_ASSERT(mClosed);
+ MOZ_ASSERT(!mCallback);
+ MOZ_ASSERT(!mWorkerPrivate);
+}
+
+void
+SendStreamChildImpl::Start()
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate);
+ DoRead();
+}
+
+void
+SendStreamChildImpl::StartDestroy()
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ OnEnd(NS_ERROR_ABORT);
+}
+
+bool
+SendStreamChildImpl::AddAsWorkerHolder(WorkerPrivate* aWorkerPrivate)
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ MOZ_ASSERT(aWorkerPrivate);
+ bool result = HoldWorker(aWorkerPrivate, Canceling);
+ if (result) {
+ mWorkerPrivate = aWorkerPrivate;
+ }
+ return result;
+}
+
+void
+SendStreamChildImpl::ActorDestroy(ActorDestroyReason aReason)
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+
+ // If the parent side runs into a problem it will ask the child to
+ // close the connection via RequestClose(). Therefore OnEnd() should
+ // always run before the actor is destroyed.
+ MOZ_ASSERT(mClosed);
+
+ if (mCallback) {
+ mCallback->ClearActor();
+ mCallback = nullptr;
+ }
+
+ if (mWorkerPrivate) {
+ ReleaseWorker();
+ mWorkerPrivate = nullptr;
+ }
+}
+
+bool
+SendStreamChildImpl::RecvRequestClose(const nsresult& aRv)
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ OnEnd(aRv);
+ return true;
+}
+
+bool
+SendStreamChildImpl::Notify(Status aStatus)
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+
+ // Keep the worker thread alive until the stream is finished.
+ return true;
+}
+
+void
+SendStreamChildImpl::DoRead()
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ MOZ_ASSERT(!mClosed);
+ MOZ_ASSERT(!mCallback);
+
+ // The input stream (likely a pipe) probably uses a segment size of
+ // 4kb. If there is data already buffered it would be nice to aggregate
+ // multiple segments into a single IPC call. Conversely, don't send too
+ // too large of a buffer in a single call to avoid spiking memory.
+ static const uint64_t kMaxBytesPerMessage = 32 * 1024;
+ static_assert(kMaxBytesPerMessage <= static_cast<uint64_t>(UINT32_MAX),
+ "kMaxBytesPerMessage must cleanly cast to uint32_t");
+
+ while (true) {
+ // It should not be possible to transition to closed state without
+ // this loop terminating via a return.
+ MOZ_ASSERT(!mClosed);
+
+ // Use non-auto here as we're unlikely to hit stack storage with the
+ // sizes we are sending. Also, it would be nice to avoid another copy
+ // to the IPC layer which we avoid if we use COW strings. Unfortunately
+ // IPC does not seem to support passing dependent storage types.
+ nsCString buffer;
+
+ uint64_t available = 0;
+ nsresult rv = mStream->Available(&available);
+ if (NS_FAILED(rv)) {
+ OnEnd(rv);
+ return;
+ }
+
+ if (available == 0) {
+ Wait();
+ return;
+ }
+
+ uint32_t expectedBytes =
+ static_cast<uint32_t>(std::min(available, kMaxBytesPerMessage));
+
+ buffer.SetLength(expectedBytes);
+
+ uint32_t bytesRead = 0;
+ rv = mStream->Read(buffer.BeginWriting(), buffer.Length(), &bytesRead);
+ MOZ_ASSERT_IF(NS_FAILED(rv), bytesRead == 0);
+ buffer.SetLength(bytesRead);
+
+ // If we read any data from the stream, send it across.
+ if (!buffer.IsEmpty()) {
+ Unused << SendBuffer(buffer);
+ }
+
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ Wait();
+ return;
+ }
+
+ // Any other error or zero-byte read indicates end-of-stream
+ if (NS_FAILED(rv) || buffer.IsEmpty()) {
+ OnEnd(rv);
+ return;
+ }
+ }
+}
+
+void
+SendStreamChildImpl::Wait()
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ MOZ_ASSERT(!mClosed);
+ MOZ_ASSERT(!mCallback);
+
+ // Set mCallback immediately instead of waiting for success. Its possible
+ // AsyncWait() will callback synchronously.
+ mCallback = new Callback(this);
+ nsresult rv = mStream->AsyncWait(mCallback, 0, 0, nullptr);
+ if (NS_FAILED(rv)) {
+ OnEnd(rv);
+ return;
+ }
+}
+
+void
+SendStreamChildImpl::OnStreamReady(Callback* aCallback)
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ MOZ_ASSERT(mCallback);
+ MOZ_ASSERT(aCallback == mCallback);
+ mCallback->ClearActor();
+ mCallback = nullptr;
+ DoRead();
+}
+
+void
+SendStreamChildImpl::OnEnd(nsresult aRv)
+{
+ NS_ASSERT_OWNINGTHREAD(SendStreamChild);
+ MOZ_ASSERT(aRv != NS_BASE_STREAM_WOULD_BLOCK);
+
+ if (mClosed) {
+ return;
+ }
+
+ mClosed = true;
+
+ mStream->CloseWithStatus(aRv);
+
+ if (aRv == NS_BASE_STREAM_CLOSED) {
+ aRv = NS_OK;
+ }
+
+ // This will trigger an ActorDestroy() from the parent side
+ Unused << SendClose(aRv);
+}
+
+bool
+IsBlocking(nsIAsyncInputStream* aInputStream)
+{
+ bool nonBlocking = false;
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aInputStream->IsNonBlocking(&nonBlocking)));
+ return !nonBlocking;
+}
+
+} // anonymous namespace
+
+// static
+SendStreamChild*
+SendStreamChild::Create(nsIAsyncInputStream* aInputStream,
+ nsIContentChild* aManager)
+{
+ MOZ_ASSERT(aInputStream);
+ MOZ_ASSERT(aManager);
+
+ // PContent can only be used on the main thread
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // SendStreamChild reads in the current thread, so it is only supported
+ // on non-blocking, async channels
+ if (NS_WARN_IF(IsBlocking(aInputStream))) {
+ return nullptr;
+ }
+
+ SendStreamChild* actor = new SendStreamChildImpl(aInputStream);
+ aManager->SendPSendStreamConstructor(actor);
+
+ return actor;
+}
+
+// static
+SendStreamChild*
+SendStreamChild::Create(nsIAsyncInputStream* aInputStream,
+ PBackgroundChild* aManager)
+{
+ MOZ_ASSERT(aInputStream);
+ MOZ_ASSERT(aManager);
+
+ // PBackground can be used on any thread, but we only support SendStream on
+ // main thread and Worker threads right now. This is due to the requirement
+ // that the thread be guaranteed to live long enough to receive messages
+ // sent from parent to child. We can enforce this guarantee with a feature
+ // on worker threads, but not other threads.
+ WorkerPrivate* workerPrivate = nullptr;
+ if (!NS_IsMainThread()) {
+ workerPrivate = GetCurrentThreadWorkerPrivate();
+ MOZ_ASSERT(workerPrivate);
+ }
+
+ // SendStreamChild reads in the current thread, so it is only supported
+ // on non-blocking, async channels
+ if (NS_WARN_IF(IsBlocking(aInputStream))) {
+ return nullptr;
+ }
+
+ SendStreamChildImpl* actor = new SendStreamChildImpl(aInputStream);
+
+ if (workerPrivate && !actor->AddAsWorkerHolder(workerPrivate)) {
+ delete actor;
+ return nullptr;
+ }
+
+ aManager->SendPSendStreamConstructor(actor);
+ return actor;
+}
+
+SendStreamChild::~SendStreamChild()
+{
+}
+
+void
+DeallocPSendStreamChild(PSendStreamChild* aActor)
+{
+ delete aActor;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/SendStreamParent.cpp b/ipc/glue/SendStreamParent.cpp
new file mode 100644
index 000000000..3ed2d1b2b
--- /dev/null
+++ b/ipc/glue/SendStreamParent.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/SendStream.h"
+
+#include "mozilla/Unused.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIPipe.h"
+
+namespace mozilla {
+namespace ipc {
+
+namespace {
+
+class SendStreamParentImpl final : public SendStreamParent
+{
+public:
+ SendStreamParentImpl(nsIAsyncInputStream* aReader,
+ nsIAsyncOutputStream* aWriter);
+ ~SendStreamParentImpl();
+
+private:
+ // PSendStreamParentImpl methods
+ virtual void
+ ActorDestroy(ActorDestroyReason aReason) override;
+
+ // SendStreamparent methods
+ already_AddRefed<nsIInputStream>
+ TakeReader() override;
+
+ virtual bool
+ RecvBuffer(const nsCString& aBuffer) override;
+
+ virtual bool
+ RecvClose(const nsresult& aRv) override;
+
+ nsCOMPtr<nsIAsyncInputStream> mReader;
+ nsCOMPtr<nsIAsyncOutputStream> mWriter;
+
+ NS_DECL_OWNINGTHREAD
+};
+
+SendStreamParentImpl::~SendStreamParentImpl()
+{
+}
+
+already_AddRefed<nsIInputStream>
+SendStreamParentImpl::TakeReader()
+{
+ MOZ_ASSERT(mReader);
+ return mReader.forget();
+}
+
+void
+SendStreamParentImpl::ActorDestroy(ActorDestroyReason aReason)
+{
+ // If we were gracefully closed we should have gotten RecvClose(). In
+ // that case, the writer will already be closed and this will have no
+ // effect. This just aborts the writer in the case where the child process
+ // crashes.
+ mWriter->CloseWithStatus(NS_ERROR_ABORT);
+}
+
+bool
+SendStreamParentImpl::RecvBuffer(const nsCString& aBuffer)
+{
+ uint32_t numWritten = 0;
+
+ // This should only fail if we hit an OOM condition.
+ nsresult rv = mWriter->Write(aBuffer.get(), aBuffer.Length(), &numWritten);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ Unused << SendRequestClose(rv);
+ }
+
+ return true;
+}
+
+bool
+SendStreamParentImpl::RecvClose(const nsresult& aRv)
+{
+ mWriter->CloseWithStatus(aRv);
+ Unused << Send__delete__(this);
+ return true;
+}
+
+SendStreamParentImpl::SendStreamParentImpl(nsIAsyncInputStream* aReader,
+ nsIAsyncOutputStream* aWriter)
+ : mReader(aReader)
+ , mWriter(aWriter)
+{
+ MOZ_ASSERT(mReader);
+ MOZ_ASSERT(mWriter);
+}
+
+} // anonymous namespace
+
+SendStreamParent::~SendStreamParent()
+{
+}
+
+PSendStreamParent*
+AllocPSendStreamParent()
+{
+ // use async versions for both reader and writer even though we are
+ // opening the writer as an infinite stream. We want to be able to
+ // use CloseWithStatus() to communicate errors through the pipe.
+ nsCOMPtr<nsIAsyncInputStream> reader;
+ nsCOMPtr<nsIAsyncOutputStream> writer;
+
+ // Use an "infinite" pipe because we cannot apply back-pressure through
+ // the async IPC layer at the moment. Blocking the IPC worker thread
+ // is not desirable, either.
+ nsresult rv = NS_NewPipe2(getter_AddRefs(reader),
+ getter_AddRefs(writer),
+ true, true, // non-blocking
+ 0, // segment size
+ UINT32_MAX); // "infinite" pipe
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ return new SendStreamParentImpl(reader, writer);
+}
+
+void
+DeallocPSendStreamParent(PSendStreamParent* aActor)
+{
+ delete aActor;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/SharedMemory.cpp b/ipc/glue/SharedMemory.cpp
new file mode 100644
index 000000000..afc8894d0
--- /dev/null
+++ b/ipc/glue/SharedMemory.cpp
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <math.h>
+
+#include "nsString.h"
+#include "nsIMemoryReporter.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/Atomics.h"
+
+namespace mozilla {
+namespace ipc {
+
+static Atomic<size_t> gShmemAllocated;
+static Atomic<size_t> gShmemMapped;
+
+class ShmemReporter final : public nsIMemoryReporter
+{
+ ~ShmemReporter() {}
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "shmem-allocated", KIND_OTHER, UNITS_BYTES, gShmemAllocated,
+ "Memory shared with other processes that is accessible (but not "
+ "necessarily mapped).");
+
+ MOZ_COLLECT_REPORT(
+ "shmem-mapped", KIND_OTHER, UNITS_BYTES, gShmemMapped,
+ "Memory shared with other processes that is mapped into the address "
+ "space.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(ShmemReporter, nsIMemoryReporter)
+
+SharedMemory::SharedMemory()
+ : mAllocSize(0)
+ , mMappedSize(0)
+{
+ static Atomic<bool> registered;
+ if (registered.compareExchange(false, true)) {
+ RegisterStrongMemoryReporter(new ShmemReporter());
+ }
+}
+
+/*static*/ size_t
+SharedMemory::PageAlignedSize(size_t aSize)
+{
+ size_t pageSize = SystemPageSize();
+ size_t nPagesNeeded = size_t(ceil(double(aSize) / double(pageSize)));
+ return pageSize * nPagesNeeded;
+}
+
+void
+SharedMemory::Created(size_t aNBytes)
+{
+ mAllocSize = aNBytes;
+ gShmemAllocated += mAllocSize;
+}
+
+void
+SharedMemory::Mapped(size_t aNBytes)
+{
+ mMappedSize = aNBytes;
+ gShmemMapped += mMappedSize;
+}
+
+void
+SharedMemory::Unmapped()
+{
+ MOZ_ASSERT(gShmemMapped >= mMappedSize,
+ "Can't unmap more than mapped");
+ gShmemMapped -= mMappedSize;
+ mMappedSize = 0;
+}
+
+/*static*/ void
+SharedMemory::Destroyed()
+{
+ MOZ_ASSERT(gShmemAllocated >= mAllocSize,
+ "Can't destroy more than allocated");
+ gShmemAllocated -= mAllocSize;
+ mAllocSize = 0;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/SharedMemory.h b/ipc/glue/SharedMemory.h
new file mode 100644
index 000000000..82f89ae4b
--- /dev/null
+++ b/ipc/glue/SharedMemory.h
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_SharedMemory_h
+#define mozilla_ipc_SharedMemory_h
+
+#include "nsDebug.h"
+#include "nsISupportsImpl.h" // NS_INLINE_DECL_REFCOUNTING
+#include "mozilla/Attributes.h"
+
+#include "base/process.h"
+#include "chrome/common/ipc_message_utils.h"
+
+//
+// This is a low-level wrapper around platform shared memory. Don't
+// use it directly; use Shmem allocated through IPDL interfaces.
+//
+namespace {
+enum Rights {
+ RightsNone = 0,
+ RightsRead = 1 << 0,
+ RightsWrite = 1 << 1
+};
+} // namespace
+
+namespace mozilla {
+
+namespace ipc {
+class SharedMemory;
+} // namespace ipc
+
+namespace ipc {
+
+class SharedMemory
+{
+protected:
+ virtual ~SharedMemory()
+ {
+ Unmapped();
+ Destroyed();
+ }
+
+public:
+ enum SharedMemoryType {
+ TYPE_BASIC,
+ TYPE_UNKNOWN
+ };
+
+ size_t Size() const { return mMappedSize; }
+
+ virtual void* memory() const = 0;
+
+ virtual bool Create(size_t size) = 0;
+ virtual bool Map(size_t nBytes) = 0;
+
+ virtual void CloseHandle() = 0;
+
+ virtual SharedMemoryType Type() const = 0;
+
+ virtual bool ShareHandle(base::ProcessId aProcessId, IPC::Message* aMessage) = 0;
+ virtual bool ReadHandle(const IPC::Message* aMessage, PickleIterator* aIter) = 0;
+
+ void
+ Protect(char* aAddr, size_t aSize, int aRights)
+ {
+ char* memStart = reinterpret_cast<char*>(memory());
+ if (!memStart)
+ NS_RUNTIMEABORT("SharedMemory region points at NULL!");
+ char* memEnd = memStart + Size();
+
+ char* protStart = aAddr;
+ if (!protStart)
+ NS_RUNTIMEABORT("trying to Protect() a NULL region!");
+ char* protEnd = protStart + aSize;
+
+ if (!(memStart <= protStart
+ && protEnd <= memEnd))
+ NS_RUNTIMEABORT("attempt to Protect() a region outside this SharedMemory");
+
+ // checks alignment etc.
+ SystemProtect(aAddr, aSize, aRights);
+ }
+
+ // bug 1168843, compositor thread may create shared memory instances that are destroyed by main thread on shutdown, so this must use thread-safe RC to avoid hitting assertion
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedMemory)
+
+ static void SystemProtect(char* aAddr, size_t aSize, int aRights);
+ static size_t SystemPageSize();
+ static size_t PageAlignedSize(size_t aSize);
+
+protected:
+ SharedMemory();
+
+ // Implementations should call these methods on shmem usage changes,
+ // but *only if* the OS-specific calls are known to have succeeded.
+ // The methods are expected to be called in the pattern
+ //
+ // Created (Mapped Unmapped)* Destroy
+ //
+ // but this isn't checked.
+ void Created(size_t aNBytes);
+ void Mapped(size_t aNBytes);
+ void Unmapped();
+ void Destroyed();
+
+ // The size of the shmem region requested in Create(), if
+ // successful. SharedMemory instances that are opened from a
+ // foreign handle have an alloc size of 0, even though they have
+ // access to the alloc-size information.
+ size_t mAllocSize;
+ // The size of the region mapped in Map(), if successful. All
+ // SharedMemorys that are mapped have a non-zero mapped size.
+ size_t mMappedSize;
+};
+
+template<typename HandleImpl>
+class SharedMemoryCommon : public SharedMemory
+{
+public:
+ typedef HandleImpl Handle;
+
+ virtual bool ShareToProcess(base::ProcessId aProcessId, Handle* aHandle) = 0;
+ virtual bool IsHandleValid(const Handle& aHandle) const = 0;
+ virtual bool SetHandle(const Handle& aHandle) = 0;
+
+ virtual bool ShareHandle(base::ProcessId aProcessId, IPC::Message* aMessage) override
+ {
+ Handle handle;
+ if (!ShareToProcess(aProcessId, &handle)) {
+ return false;
+ }
+ IPC::WriteParam(aMessage, handle);
+ return true;
+ }
+
+ virtual bool ReadHandle(const IPC::Message* aMessage, PickleIterator* aIter) override
+ {
+ Handle handle;
+ return IPC::ReadParam(aMessage, aIter, &handle) &&
+ IsHandleValid(handle) &&
+ SetHandle(handle);
+ }
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_ipc_SharedMemory_h
diff --git a/ipc/glue/SharedMemoryBasic.h b/ipc/glue/SharedMemoryBasic.h
new file mode 100644
index 000000000..d8720271f
--- /dev/null
+++ b/ipc/glue/SharedMemoryBasic.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_SharedMemoryBasic_h
+#define mozilla_ipc_SharedMemoryBasic_h
+
+#ifdef ANDROID
+# include "mozilla/ipc/SharedMemoryBasic_android.h"
+#elif defined(XP_DARWIN)
+# include "mozilla/ipc/SharedMemoryBasic_mach.h"
+#else
+# include "mozilla/ipc/SharedMemoryBasic_chromium.h"
+#endif
+
+#endif // ifndef mozilla_ipc_SharedMemoryBasic_h
diff --git a/ipc/glue/SharedMemoryBasic_android.cpp b/ipc/glue/SharedMemoryBasic_android.cpp
new file mode 100644
index 000000000..05c9ca9ad
--- /dev/null
+++ b/ipc/glue/SharedMemoryBasic_android.cpp
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <android/log.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/process_util.h"
+
+#include "SharedMemoryBasic.h"
+
+//
+// Temporarily go directly to the kernel interface until we can
+// interact better with libcutils.
+//
+#include <linux/ashmem.h>
+
+namespace mozilla {
+namespace ipc {
+
+static void
+LogError(const char* what)
+{
+ __android_log_print(ANDROID_LOG_ERROR, "Gecko",
+ "%s: %s (%d)", what, strerror(errno), errno);
+}
+
+SharedMemoryBasic::SharedMemoryBasic()
+ : mShmFd(-1)
+ , mMemory(nullptr)
+{ }
+
+SharedMemoryBasic::~SharedMemoryBasic()
+{
+ Unmap();
+ CloseHandle();
+}
+
+bool
+SharedMemoryBasic::SetHandle(const Handle& aHandle)
+{
+ MOZ_ASSERT(-1 == mShmFd, "Already Create()d");
+ mShmFd = aHandle.fd;
+ return true;
+}
+
+bool
+SharedMemoryBasic::Create(size_t aNbytes)
+{
+ MOZ_ASSERT(-1 == mShmFd, "Already Create()d");
+
+ // Carve a new instance off of /dev/ashmem
+ int shmfd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
+ if (-1 == shmfd) {
+ LogError("ShmemAndroid::Create():open");
+ return false;
+ }
+
+ if (ioctl(shmfd, ASHMEM_SET_SIZE, aNbytes)) {
+ LogError("ShmemAndroid::Unmap():ioctl(SET_SIZE)");
+ close(shmfd);
+ return false;
+ }
+
+ mShmFd = shmfd;
+ Created(aNbytes);
+ return true;
+}
+
+bool
+SharedMemoryBasic::Map(size_t nBytes)
+{
+ MOZ_ASSERT(nullptr == mMemory, "Already Map()d");
+
+ mMemory = mmap(nullptr, nBytes,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ mShmFd,
+ 0);
+ if (MAP_FAILED == mMemory) {
+ LogError("ShmemAndroid::Map()");
+ mMemory = nullptr;
+ return false;
+ }
+
+ Mapped(nBytes);
+ return true;
+}
+
+bool
+SharedMemoryBasic::ShareToProcess(base::ProcessId/*unused*/,
+ Handle* aNewHandle)
+{
+ MOZ_ASSERT(mShmFd >= 0, "Should have been Create()d by now");
+
+ int shmfdDup = dup(mShmFd);
+ if (-1 == shmfdDup) {
+ LogError("ShmemAndroid::ShareToProcess()");
+ return false;
+ }
+
+ aNewHandle->fd = shmfdDup;
+ aNewHandle->auto_close = true;
+ return true;
+}
+
+void
+SharedMemoryBasic::Unmap()
+{
+ if (!mMemory) {
+ return;
+ }
+
+ if (munmap(mMemory, Size())) {
+ LogError("ShmemAndroid::Unmap()");
+ }
+ mMemory = nullptr;
+}
+
+void
+SharedMemoryBasic::CloseHandle()
+{
+ if (mShmFd != -1) {
+ close(mShmFd);
+ mShmFd = -1;
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/SharedMemoryBasic_android.h b/ipc/glue/SharedMemoryBasic_android.h
new file mode 100644
index 000000000..488390d9f
--- /dev/null
+++ b/ipc/glue/SharedMemoryBasic_android.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_SharedMemoryBasic_android_h
+#define mozilla_ipc_SharedMemoryBasic_android_h
+
+#include "base/file_descriptor_posix.h"
+
+#include "SharedMemory.h"
+
+//
+// This is a low-level wrapper around platform shared memory. Don't
+// use it directly; use Shmem allocated through IPDL interfaces.
+//
+
+namespace mozilla {
+namespace ipc {
+
+class SharedMemoryBasic final : public SharedMemoryCommon<base::FileDescriptor>
+{
+public:
+ SharedMemoryBasic();
+
+ virtual bool SetHandle(const Handle& aHandle) override;
+
+ virtual bool Create(size_t aNbytes) override;
+
+ virtual bool Map(size_t nBytes) override;
+
+ virtual void CloseHandle() override;
+
+ virtual void* memory() const override
+ {
+ return mMemory;
+ }
+
+ virtual SharedMemoryType Type() const override
+ {
+ return TYPE_BASIC;
+ }
+
+ static Handle NULLHandle()
+ {
+ return Handle();
+ }
+
+ virtual bool IsHandleValid(const Handle &aHandle) const override
+ {
+ return aHandle.fd >= 0;
+ }
+
+ virtual bool ShareToProcess(base::ProcessId aProcessId,
+ Handle* aNewHandle) override;
+
+private:
+ ~SharedMemoryBasic();
+
+ void Unmap();
+
+ // The /dev/ashmem fd we allocate.
+ int mShmFd;
+ // Pointer to mapped region, null if unmapped.
+ void *mMemory;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // ifndef mozilla_ipc_SharedMemoryBasic_android_h
diff --git a/ipc/glue/SharedMemoryBasic_chromium.h b/ipc/glue/SharedMemoryBasic_chromium.h
new file mode 100644
index 000000000..b930a6e66
--- /dev/null
+++ b/ipc/glue/SharedMemoryBasic_chromium.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_SharedMemoryBasic_chromium_h
+#define mozilla_ipc_SharedMemoryBasic_chromium_h
+
+#include "base/shared_memory.h"
+#include "SharedMemory.h"
+
+#include "nsDebug.h"
+
+//
+// This is a low-level wrapper around platform shared memory. Don't
+// use it directly; use Shmem allocated through IPDL interfaces.
+//
+
+namespace mozilla {
+namespace ipc {
+
+class SharedMemoryBasic final : public SharedMemoryCommon<base::SharedMemoryHandle>
+{
+public:
+ SharedMemoryBasic()
+ {
+ }
+
+ virtual bool SetHandle(const Handle& aHandle) override {
+ return mSharedMemory.SetHandle(aHandle, false);
+ }
+
+ virtual bool Create(size_t aNbytes) override
+ {
+ bool ok = mSharedMemory.Create("", false, false, aNbytes);
+ if (ok) {
+ Created(aNbytes);
+ }
+ return ok;
+ }
+
+ virtual bool Map(size_t nBytes) override
+ {
+ bool ok = mSharedMemory.Map(nBytes);
+ if (ok) {
+ Mapped(nBytes);
+ }
+ return ok;
+ }
+
+ virtual void CloseHandle() override
+ {
+ mSharedMemory.Close(false);
+ }
+
+ virtual void* memory() const override
+ {
+ return mSharedMemory.memory();
+ }
+
+ virtual SharedMemoryType Type() const override
+ {
+ return TYPE_BASIC;
+ }
+
+ static Handle NULLHandle()
+ {
+ return base::SharedMemory::NULLHandle();
+ }
+
+ virtual bool IsHandleValid(const Handle &aHandle) const override
+ {
+ return base::SharedMemory::IsHandleValid(aHandle);
+ }
+
+ virtual bool ShareToProcess(base::ProcessId aProcessId,
+ Handle* new_handle) override
+ {
+ base::SharedMemoryHandle handle;
+ bool ret = mSharedMemory.ShareToProcess(aProcessId, &handle);
+ if (ret)
+ *new_handle = handle;
+ return ret;
+ }
+
+private:
+ ~SharedMemoryBasic()
+ {
+ }
+
+ base::SharedMemory mSharedMemory;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+
+#endif // ifndef mozilla_ipc_SharedMemoryBasic_chromium_h
diff --git a/ipc/glue/SharedMemoryBasic_mach.h b/ipc/glue/SharedMemoryBasic_mach.h
new file mode 100644
index 000000000..0b03683ef
--- /dev/null
+++ b/ipc/glue/SharedMemoryBasic_mach.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_ipc_SharedMemoryBasic_mach_h
+#define mozilla_ipc_SharedMemoryBasic_mach_h
+
+#include "base/file_descriptor_posix.h"
+#include "base/process.h"
+
+#include "SharedMemory.h"
+#include <mach/port.h>
+
+//
+// This is a low-level wrapper around platform shared memory. Don't
+// use it directly; use Shmem allocated through IPDL interfaces.
+//
+
+class MachPortSender;
+class ReceivePort;
+
+namespace mozilla {
+namespace ipc {
+
+class SharedMemoryBasic final : public SharedMemoryCommon<mach_port_t>
+{
+public:
+ static void SetupMachMemory(pid_t pid,
+ ReceivePort* listen_port,
+ MachPortSender* listen_port_ack,
+ MachPortSender* send_port,
+ ReceivePort* send_port_ack,
+ bool pidIsParent);
+
+ static void CleanupForPid(pid_t pid);
+
+ static void Shutdown();
+
+ SharedMemoryBasic();
+
+ virtual bool SetHandle(const Handle& aHandle) override;
+
+ virtual bool Create(size_t aNbytes) override;
+
+ virtual bool Map(size_t nBytes) override;
+
+ virtual void CloseHandle() override;
+
+ virtual void* memory() const override
+ {
+ return mMemory;
+ }
+
+ virtual SharedMemoryType Type() const override
+ {
+ return TYPE_BASIC;
+ }
+
+ static Handle NULLHandle()
+ {
+ return Handle();
+ }
+
+
+ virtual bool IsHandleValid(const Handle &aHandle) const override;
+
+ virtual bool ShareToProcess(base::ProcessId aProcessId,
+ Handle* aNewHandle) override;
+
+private:
+ ~SharedMemoryBasic();
+
+ void Unmap();
+ mach_port_t mPort;
+ // Pointer to mapped region, null if unmapped.
+ void *mMemory;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // ifndef mozilla_ipc_SharedMemoryBasic_mach_h
diff --git a/ipc/glue/SharedMemoryBasic_mach.mm b/ipc/glue/SharedMemoryBasic_mach.mm
new file mode 100644
index 000000000..88753ee2e
--- /dev/null
+++ b/ipc/glue/SharedMemoryBasic_mach.mm
@@ -0,0 +1,676 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <map>
+
+#include <mach/vm_map.h>
+#include <mach/mach_port.h>
+#if defined(XP_IOS)
+#include <mach/vm_map.h>
+#define mach_vm_address_t vm_address_t
+#define mach_vm_allocate vm_allocate
+#define mach_vm_deallocate vm_deallocate
+#define mach_vm_map vm_map
+#define mach_vm_read vm_read
+#define mach_vm_region_recurse vm_region_recurse_64
+#define mach_vm_size_t vm_size_t
+#else
+#include <mach/mach_vm.h>
+#endif
+#include <pthread.h>
+#include <unistd.h>
+#include "SharedMemoryBasic.h"
+#include "chrome/common/mach_ipc_mac.h"
+
+#include "mozilla/StaticMutex.h"
+
+#ifdef DEBUG
+#define LOG_ERROR(str, args...) \
+ PR_BEGIN_MACRO \
+ char *msg = PR_smprintf(str, ## args); \
+ NS_WARNING(msg); \
+ PR_smprintf_free(msg); \
+ PR_END_MACRO
+#else
+#define LOG_ERROR(str, args...) do { /* nothing */ } while(0)
+#endif
+
+#define CHECK_MACH_ERROR(kr, msg) \
+ PR_BEGIN_MACRO \
+ if (kr != KERN_SUCCESS) { \
+ LOG_ERROR("%s %s (%x)\n", msg, mach_error_string(kr), kr); \
+ return false; \
+ } \
+ PR_END_MACRO
+
+/*
+ * This code is responsible for sharing memory between processes. Memory can be
+ * shared between parent and child or between two children. Each memory region is
+ * referenced via a Mach port. Mach ports are also used for messaging when
+ * sharing a memory region.
+ *
+ * When the parent starts a child, it starts a thread whose only purpose is to
+ * communicate with the child about shared memory. Once the child has started,
+ * it starts a similar thread for communicating with the parent. Each side can
+ * communicate with the thread on the other side via Mach ports. When either
+ * side wants to share memory with the other, it sends a Mach message to the
+ * other side. Attached to the message is the port that references the shared
+ * memory region. When the other side receives the message, it automatically
+ * gets access to the region. It sends a reply (also via a Mach port) so that
+ * the originating side can continue.
+ *
+ * The two sides communicate using four ports. Two ports are used when the
+ * parent shares memory with the child. The other two are used when the child
+ * shares memory with the parent. One of these two ports is used for sending the
+ * "share" message and the other is used for the reply.
+ *
+ * If a child wants to share memory with another child, it sends a "GetPorts"
+ * message to the parent. The parent forwards this GetPorts message to the
+ * target child. The message includes some ports so that the children can talk
+ * directly. Both children start up a thread to communicate with the other child,
+ * similar to the way parent and child communicate. In the future, when these
+ * two children want to communicate, they re-use the channels that were created.
+ *
+ * When a child shuts down, the parent notifies all other children. Those
+ * children then have the opportunity to shut down any threads they might have
+ * been using to communicate directly with that child.
+ */
+
+namespace mozilla {
+namespace ipc {
+
+struct MemoryPorts {
+ MachPortSender* mSender;
+ ReceivePort* mReceiver;
+
+ MemoryPorts() {}
+ MemoryPorts(MachPortSender* sender, ReceivePort* receiver)
+ : mSender(sender), mReceiver(receiver) {}
+};
+
+// Protects gMemoryCommPorts and gThreads.
+static StaticMutex gMutex;
+
+static std::map<pid_t, MemoryPorts> gMemoryCommPorts;
+
+enum {
+ kGetPortsMsg = 1,
+ kSharePortsMsg,
+ kReturnIdMsg,
+ kReturnPortsMsg,
+ kShutdownMsg,
+ kCleanupMsg,
+};
+
+const int kTimeout = 1000;
+const int kLongTimeout = 60 * kTimeout;
+
+pid_t gParentPid = 0;
+
+struct PIDPair {
+ pid_t mRequester;
+ pid_t mRequested;
+
+ PIDPair(pid_t requester, pid_t requested)
+ : mRequester(requester), mRequested(requested) {}
+};
+
+struct ListeningThread {
+ pthread_t mThread;
+ MemoryPorts* mPorts;
+
+ ListeningThread() {}
+ ListeningThread(pthread_t thread, MemoryPorts* ports)
+ : mThread(thread), mPorts(ports) {}
+};
+
+struct SharePortsReply {
+ uint64_t serial;
+ mach_port_t port;
+};
+
+std::map<pid_t, ListeningThread> gThreads;
+
+static void *
+PortServerThread(void *argument);
+
+
+static void
+SetupMachMemory(pid_t pid,
+ ReceivePort* listen_port,
+ MachPortSender* listen_port_ack,
+ MachPortSender* send_port,
+ ReceivePort* send_port_ack,
+ bool pidIsParent)
+{
+ if (pidIsParent) {
+ gParentPid = pid;
+ }
+ MemoryPorts* listen_ports = new MemoryPorts(listen_port_ack, listen_port);
+ pthread_t thread;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ int err = pthread_create(&thread, &attr, PortServerThread, listen_ports);
+ if (err) {
+ LOG_ERROR("pthread_create failed with %x\n", err);
+ return;
+ }
+
+ gMutex.AssertCurrentThreadOwns();
+ gThreads[pid] = ListeningThread(thread, listen_ports);
+ gMemoryCommPorts[pid] = MemoryPorts(send_port, send_port_ack);
+}
+
+// Send two communication ports to another process along with the pid of the process that is
+// listening on them.
+bool
+SendPortsMessage(MachPortSender* sender,
+ mach_port_t ports_in_receiver,
+ mach_port_t ports_out_receiver,
+ PIDPair pid_pair)
+{
+ MachSendMessage getPortsMsg(kGetPortsMsg);
+ if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(ports_in_receiver))) {
+ LOG_ERROR("Adding descriptor to message failed");
+ return false;
+ }
+ if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(ports_out_receiver))) {
+ LOG_ERROR("Adding descriptor to message failed");
+ return false;
+ }
+
+ getPortsMsg.SetData(&pid_pair, sizeof(PIDPair));
+ kern_return_t err = sender->SendMessage(getPortsMsg, kTimeout);
+ if (KERN_SUCCESS != err) {
+ LOG_ERROR("Error sending get ports message %s (%x)\n", mach_error_string(err), err);
+ return false;
+ }
+ return true;
+}
+
+// Receive two communication ports from another process
+bool
+RecvPortsMessage(ReceivePort* receiver, mach_port_t* ports_in_sender, mach_port_t* ports_out_sender)
+{
+ MachReceiveMessage rcvPortsMsg;
+ kern_return_t err = receiver->WaitForMessage(&rcvPortsMsg, kTimeout);
+ if (KERN_SUCCESS != err) {
+ LOG_ERROR("Error receiving get ports message %s (%x)\n", mach_error_string(err), err);
+ }
+ if (rcvPortsMsg.GetTranslatedPort(0) == MACH_PORT_NULL) {
+ LOG_ERROR("GetTranslatedPort(0) failed");
+ return false;
+ }
+ *ports_in_sender = rcvPortsMsg.GetTranslatedPort(0);
+
+ if (rcvPortsMsg.GetTranslatedPort(1) == MACH_PORT_NULL) {
+ LOG_ERROR("GetTranslatedPort(1) failed");
+ return false;
+ }
+ *ports_out_sender = rcvPortsMsg.GetTranslatedPort(1);
+ return true;
+}
+
+// Send two communication ports to another process and receive two back
+bool
+RequestPorts(const MemoryPorts& request_ports,
+ mach_port_t ports_in_receiver,
+ mach_port_t* ports_in_sender,
+ mach_port_t* ports_out_sender,
+ mach_port_t ports_out_receiver,
+ PIDPair pid_pair)
+{
+ if (!SendPortsMessage(request_ports.mSender, ports_in_receiver, ports_out_receiver, pid_pair)) {
+ return false;
+ }
+ return RecvPortsMessage(request_ports.mReceiver, ports_in_sender, ports_out_sender);
+}
+
+MemoryPorts*
+GetMemoryPortsForPid(pid_t pid)
+{
+ gMutex.AssertCurrentThreadOwns();
+
+ if (gMemoryCommPorts.find(pid) == gMemoryCommPorts.end()) {
+ // We don't have the ports open to communicate with that pid, so we're going to
+ // ask our parent process over IPC to set them up for us.
+ if (gParentPid == 0) {
+ // If we're the top level parent process, we have no parent to ask.
+ LOG_ERROR("request for ports for pid %d, but we're the chrome process\n", pid);
+ return nullptr;
+ }
+ const MemoryPorts& parent = gMemoryCommPorts[gParentPid];
+
+ // Create two receiving ports in this process to send to the parent. One will be used for
+ // for listening for incoming memory to be shared, the other for getting the Handle of
+ // memory we share to the other process.
+ ReceivePort* ports_in_receiver = new ReceivePort();
+ ReceivePort* ports_out_receiver = new ReceivePort();
+ mach_port_t raw_ports_in_sender, raw_ports_out_sender;
+ if (!RequestPorts(parent,
+ ports_in_receiver->GetPort(),
+ &raw_ports_in_sender,
+ &raw_ports_out_sender,
+ ports_out_receiver->GetPort(),
+ PIDPair(getpid(), pid))) {
+ LOG_ERROR("failed to request ports\n");
+ return nullptr;
+ }
+ // Our parent process sent us two ports, one is for sending new memory to, the other
+ // is for replying with the Handle when we receive new memory.
+ MachPortSender* ports_in_sender = new MachPortSender(raw_ports_in_sender);
+ MachPortSender* ports_out_sender = new MachPortSender(raw_ports_out_sender);
+ SetupMachMemory(pid,
+ ports_in_receiver,
+ ports_in_sender,
+ ports_out_sender,
+ ports_out_receiver,
+ false);
+ MOZ_ASSERT(gMemoryCommPorts.find(pid) != gMemoryCommPorts.end());
+ }
+ return &gMemoryCommPorts.at(pid);
+}
+
+// We just received a port representing a region of shared memory, reply to
+// the process that set it with the mach_port_t that represents it in this process.
+// That will be the Handle to be shared over normal IPC
+void
+HandleSharePortsMessage(MachReceiveMessage* rmsg, MemoryPorts* ports)
+{
+ mach_port_t port = rmsg->GetTranslatedPort(0);
+ uint64_t* serial = reinterpret_cast<uint64_t*>(rmsg->GetData());
+ MachSendMessage msg(kReturnIdMsg);
+ // Construct the reply message, echoing the serial, and adding the port
+ SharePortsReply replydata;
+ replydata.port = port;
+ replydata.serial = *serial;
+ msg.SetData(&replydata, sizeof(SharePortsReply));
+ kern_return_t err = ports->mSender->SendMessage(msg, kTimeout);
+ if (KERN_SUCCESS != err) {
+ LOG_ERROR("SendMessage failed 0x%x %s\n", err, mach_error_string(err));
+ }
+}
+
+// We were asked by another process to get communications ports to some process. Return
+// those ports via an IPC message.
+bool
+SendReturnPortsMsg(MachPortSender* sender,
+ mach_port_t raw_ports_in_sender,
+ mach_port_t raw_ports_out_sender)
+{
+ MachSendMessage getPortsMsg(kReturnPortsMsg);
+ if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(raw_ports_in_sender))) {
+ LOG_ERROR("Adding descriptor to message failed");
+ return false;
+ }
+
+ if (!getPortsMsg.AddDescriptor(MachMsgPortDescriptor(raw_ports_out_sender))) {
+ LOG_ERROR("Adding descriptor to message failed");
+ return false;
+ }
+ kern_return_t err = sender->SendMessage(getPortsMsg, kTimeout);
+ if (KERN_SUCCESS != err) {
+ LOG_ERROR("Error sending get ports message %s (%x)\n", mach_error_string(err), err);
+ return false;
+ }
+ return true;
+}
+
+// We were asked for communcations ports to a process that isn't us. Assuming that process
+// is one of our children, forward that request on.
+void
+ForwardGetPortsMessage(MachReceiveMessage* rmsg, MemoryPorts* ports, PIDPair* pid_pair)
+{
+ if (rmsg->GetTranslatedPort(0) == MACH_PORT_NULL) {
+ LOG_ERROR("GetTranslatedPort(0) failed");
+ return;
+ }
+ if (rmsg->GetTranslatedPort(1) == MACH_PORT_NULL) {
+ LOG_ERROR("GetTranslatedPort(1) failed");
+ return;
+ }
+ mach_port_t raw_ports_in_sender, raw_ports_out_sender;
+ MemoryPorts* requestedPorts = GetMemoryPortsForPid(pid_pair->mRequested);
+ if (!requestedPorts) {
+ LOG_ERROR("failed to find port for process\n");
+ return;
+ }
+ if (!RequestPorts(*requestedPorts, rmsg->GetTranslatedPort(0), &raw_ports_in_sender,
+ &raw_ports_out_sender, rmsg->GetTranslatedPort(1), *pid_pair)) {
+ LOG_ERROR("failed to request ports\n");
+ return;
+ }
+ SendReturnPortsMsg(ports->mSender, raw_ports_in_sender, raw_ports_out_sender);
+}
+
+// We receieved a message asking us to get communications ports for another process
+void
+HandleGetPortsMessage(MachReceiveMessage* rmsg, MemoryPorts* ports)
+{
+ PIDPair* pid_pair;
+ if (rmsg->GetDataLength() != sizeof(PIDPair)) {
+ LOG_ERROR("Improperly formatted message\n");
+ return;
+ }
+ pid_pair = reinterpret_cast<PIDPair*>(rmsg->GetData());
+ if (pid_pair->mRequested != getpid()) {
+ // This request is for ports to a process that isn't us, forward it to that process
+ ForwardGetPortsMessage(rmsg, ports, pid_pair);
+ } else {
+ if (rmsg->GetTranslatedPort(0) == MACH_PORT_NULL) {
+ LOG_ERROR("GetTranslatedPort(0) failed");
+ return;
+ }
+
+ if (rmsg->GetTranslatedPort(1) == MACH_PORT_NULL) {
+ LOG_ERROR("GetTranslatedPort(1) failed");
+ return;
+ }
+
+ MachPortSender* ports_in_sender = new MachPortSender(rmsg->GetTranslatedPort(0));
+ MachPortSender* ports_out_sender = new MachPortSender(rmsg->GetTranslatedPort(1));
+
+ ReceivePort* ports_in_receiver = new ReceivePort();
+ ReceivePort* ports_out_receiver = new ReceivePort();
+ if (SendReturnPortsMsg(ports->mSender, ports_in_receiver->GetPort(), ports_out_receiver->GetPort())) {
+ SetupMachMemory(pid_pair->mRequester,
+ ports_out_receiver,
+ ports_out_sender,
+ ports_in_sender,
+ ports_in_receiver,
+ false);
+ }
+ }
+}
+
+static void *
+PortServerThread(void *argument)
+{
+ MemoryPorts* ports = static_cast<MemoryPorts*>(argument);
+ MachReceiveMessage child_message;
+ while (true) {
+ MachReceiveMessage rmsg;
+ kern_return_t err = ports->mReceiver->WaitForMessage(&rmsg, MACH_MSG_TIMEOUT_NONE);
+ if (err != KERN_SUCCESS) {
+ LOG_ERROR("Wait for message failed 0x%x %s\n", err, mach_error_string(err));
+ continue;
+ }
+ if (rmsg.GetMessageID() == kShutdownMsg) {
+ delete ports->mSender;
+ delete ports->mReceiver;
+ delete ports;
+ return nullptr;
+ }
+ StaticMutexAutoLock smal(gMutex);
+ switch (rmsg.GetMessageID()) {
+ case kSharePortsMsg:
+ HandleSharePortsMessage(&rmsg, ports);
+ break;
+ case kGetPortsMsg:
+ HandleGetPortsMessage(&rmsg, ports);
+ break;
+ case kCleanupMsg:
+ if (gParentPid == 0) {
+ LOG_ERROR("Cleanup message not valid for parent process");
+ continue;
+ }
+
+ pid_t* pid;
+ if (rmsg.GetDataLength() != sizeof(pid_t)) {
+ LOG_ERROR("Improperly formatted message\n");
+ continue;
+ }
+ pid = reinterpret_cast<pid_t*>(rmsg.GetData());
+ SharedMemoryBasic::CleanupForPid(*pid);
+ break;
+ default:
+ LOG_ERROR("Unknown message\n");
+ }
+ }
+}
+
+void
+SharedMemoryBasic::SetupMachMemory(pid_t pid,
+ ReceivePort* listen_port,
+ MachPortSender* listen_port_ack,
+ MachPortSender* send_port,
+ ReceivePort* send_port_ack,
+ bool pidIsParent)
+{
+ StaticMutexAutoLock smal(gMutex);
+ mozilla::ipc::SetupMachMemory(pid, listen_port, listen_port_ack, send_port, send_port_ack, pidIsParent);
+}
+
+void
+SharedMemoryBasic::Shutdown()
+{
+ StaticMutexAutoLock smal(gMutex);
+
+ for (auto it = gThreads.begin(); it != gThreads.end(); ++it) {
+ MachSendMessage shutdownMsg(kShutdownMsg);
+ it->second.mPorts->mReceiver->SendMessageToSelf(shutdownMsg, kTimeout);
+ }
+ gThreads.clear();
+
+ for (auto it = gMemoryCommPorts.begin(); it != gMemoryCommPorts.end(); ++it) {
+ delete it->second.mSender;
+ delete it->second.mReceiver;
+ }
+ gMemoryCommPorts.clear();
+}
+
+void
+SharedMemoryBasic::CleanupForPid(pid_t pid)
+{
+ if (gThreads.find(pid) == gThreads.end()) {
+ return;
+ }
+ const ListeningThread& listeningThread = gThreads[pid];
+ MachSendMessage shutdownMsg(kShutdownMsg);
+ kern_return_t ret = listeningThread.mPorts->mReceiver->SendMessageToSelf(shutdownMsg, kTimeout);
+ if (ret != KERN_SUCCESS) {
+ LOG_ERROR("sending shutdown msg failed %s %x\n", mach_error_string(ret), ret);
+ }
+ gThreads.erase(pid);
+
+ if (gParentPid == 0) {
+ // We're the parent. Broadcast the cleanup message to everyone else.
+ for (auto it = gMemoryCommPorts.begin(); it != gMemoryCommPorts.end(); ++it) {
+ MachSendMessage msg(kCleanupMsg);
+ msg.SetData(&pid, sizeof(pid));
+ // We don't really care if this fails, we could be trying to send to an already shut down proc
+ it->second.mSender->SendMessage(msg, kTimeout);
+ }
+ }
+
+ MemoryPorts& ports = gMemoryCommPorts[pid];
+ delete ports.mSender;
+ delete ports.mReceiver;
+ gMemoryCommPorts.erase(pid);
+}
+
+SharedMemoryBasic::SharedMemoryBasic()
+ : mPort(MACH_PORT_NULL)
+ , mMemory(nullptr)
+{
+}
+
+SharedMemoryBasic::~SharedMemoryBasic()
+{
+ Unmap();
+ CloseHandle();
+}
+
+bool
+SharedMemoryBasic::SetHandle(const Handle& aHandle)
+{
+ MOZ_ASSERT(mPort == MACH_PORT_NULL, "already initialized");
+
+ mPort = aHandle;
+ return true;
+}
+
+static inline void*
+toPointer(mach_vm_address_t address)
+{
+ return reinterpret_cast<void*>(static_cast<uintptr_t>(address));
+}
+
+static inline mach_vm_address_t
+toVMAddress(void* pointer)
+{
+ return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(pointer));
+}
+
+bool
+SharedMemoryBasic::Create(size_t size)
+{
+ mach_vm_address_t address;
+
+ kern_return_t kr = mach_vm_allocate(mach_task_self(), &address, round_page(size), VM_FLAGS_ANYWHERE);
+ if (kr != KERN_SUCCESS) {
+ LOG_ERROR("Failed to allocate mach_vm_allocate shared memory (%zu bytes). %s (%x)\n",
+ size, mach_error_string(kr), kr);
+ return false;
+ }
+
+ memory_object_size_t memoryObjectSize = round_page(size);
+
+ kr = mach_make_memory_entry_64(mach_task_self(),
+ &memoryObjectSize,
+ address,
+ VM_PROT_DEFAULT,
+ &mPort,
+ MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ LOG_ERROR("Failed to make memory entry (%zu bytes). %s (%x)\n",
+ size, mach_error_string(kr), kr);
+ return false;
+ }
+
+ mMemory = toPointer(address);
+ Mapped(size);
+ return true;
+}
+
+bool
+SharedMemoryBasic::Map(size_t size)
+{
+ if (mMemory) {
+ return true;
+ }
+
+ if (MACH_PORT_NULL == mPort) {
+ return false;
+ }
+
+ kern_return_t kr;
+ mach_vm_address_t address = 0;
+
+ vm_prot_t vmProtection = VM_PROT_READ | VM_PROT_WRITE;
+
+ kr = mach_vm_map(mach_task_self(), &address, round_page(size), 0, VM_FLAGS_ANYWHERE,
+ mPort, 0, false, vmProtection, vmProtection, VM_INHERIT_NONE);
+ if (kr != KERN_SUCCESS) {
+ LOG_ERROR("Failed to map shared memory (%zu bytes) into %x, port %x. %s (%x)\n",
+ size, mach_task_self(), mPort, mach_error_string(kr), kr);
+ return false;
+ }
+
+ mMemory = toPointer(address);
+ Mapped(size);
+ return true;
+}
+
+bool
+SharedMemoryBasic::ShareToProcess(base::ProcessId pid,
+ Handle* aNewHandle)
+{
+ if (pid == getpid()) {
+ *aNewHandle = mPort;
+ return mach_port_mod_refs(mach_task_self(), *aNewHandle, MACH_PORT_RIGHT_SEND, 1) == KERN_SUCCESS;
+ }
+ StaticMutexAutoLock smal(gMutex);
+
+ // Serially number the messages, to check whether
+ // the reply we get was meant for us.
+ static uint64_t serial = 0;
+ uint64_t my_serial = serial;
+ serial++;
+
+ MemoryPorts* ports = GetMemoryPortsForPid(pid);
+ if (!ports) {
+ LOG_ERROR("Unable to get ports for process.\n");
+ return false;
+ }
+ MachSendMessage smsg(kSharePortsMsg);
+ smsg.AddDescriptor(MachMsgPortDescriptor(mPort, MACH_MSG_TYPE_COPY_SEND));
+ smsg.SetData(&my_serial, sizeof(uint64_t));
+ kern_return_t err = ports->mSender->SendMessage(smsg, kTimeout);
+ if (err != KERN_SUCCESS) {
+ LOG_ERROR("sending port failed %s %x\n", mach_error_string(err), err);
+ return false;
+ }
+ MachReceiveMessage msg;
+ err = ports->mReceiver->WaitForMessage(&msg, kTimeout);
+ if (err != KERN_SUCCESS) {
+ LOG_ERROR("short timeout didn't get an id %s %x\n", mach_error_string(err), err);
+ err = ports->mReceiver->WaitForMessage(&msg, kLongTimeout);
+
+ if (err != KERN_SUCCESS) {
+ LOG_ERROR("long timeout didn't get an id %s %x\n", mach_error_string(err), err);
+ return false;
+ }
+ }
+ if (msg.GetDataLength() != sizeof(SharePortsReply)) {
+ LOG_ERROR("Improperly formatted reply\n");
+ return false;
+ }
+ SharePortsReply* msg_data = reinterpret_cast<SharePortsReply*>(msg.GetData());
+ mach_port_t id = msg_data->port;
+ uint64_t serial_check = msg_data->serial;
+ if (serial_check != my_serial) {
+ LOG_ERROR("Serials do not match up: %d vs %d", serial_check, my_serial);
+ return false;
+ }
+ *aNewHandle = id;
+ return true;
+}
+
+void
+SharedMemoryBasic::Unmap()
+{
+ if (!mMemory) {
+ return;
+ }
+ vm_address_t address = toVMAddress(mMemory);
+ kern_return_t kr = vm_deallocate(mach_task_self(), address, round_page(mMappedSize));
+ if (kr != KERN_SUCCESS) {
+ LOG_ERROR("Failed to deallocate shared memory. %s (%x)\n", mach_error_string(kr), kr);
+ return;
+ }
+ mMemory = nullptr;
+}
+
+void
+SharedMemoryBasic::CloseHandle()
+{
+ if (mPort != MACH_PORT_NULL) {
+ mach_port_deallocate(mach_task_self(), mPort);
+ mPort = MACH_PORT_NULL;
+ }
+}
+
+bool
+SharedMemoryBasic::IsHandleValid(const Handle& aHandle) const
+{
+ return aHandle > 0;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/SharedMemory_posix.cpp b/ipc/glue/SharedMemory_posix.cpp
new file mode 100644
index 000000000..ca7833ce0
--- /dev/null
+++ b/ipc/glue/SharedMemory_posix.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <sys/mman.h> // mprotect
+#include <unistd.h> // sysconf
+
+#include "mozilla/ipc/SharedMemory.h"
+
+namespace mozilla {
+namespace ipc {
+
+void
+SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights)
+{
+ int flags = 0;
+ if (aRights & RightsRead)
+ flags |= PROT_READ;
+ if (aRights & RightsWrite)
+ flags |= PROT_WRITE;
+ if (RightsNone == aRights)
+ flags = PROT_NONE;
+
+ if (0 < mprotect(aAddr, aSize, flags))
+ NS_RUNTIMEABORT("can't mprotect()");
+}
+
+size_t
+SharedMemory::SystemPageSize()
+{
+ return sysconf(_SC_PAGESIZE);
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/SharedMemory_windows.cpp b/ipc/glue/SharedMemory_windows.cpp
new file mode 100644
index 000000000..f38977497
--- /dev/null
+++ b/ipc/glue/SharedMemory_windows.cpp
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+
+#include "mozilla/ipc/SharedMemory.h"
+
+namespace mozilla {
+namespace ipc {
+
+void
+SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights)
+{
+ DWORD flags;
+ if ((aRights & RightsRead) && (aRights & RightsWrite))
+ flags = PAGE_READWRITE;
+ else if (aRights & RightsRead)
+ flags = PAGE_READONLY;
+ else
+ flags = PAGE_NOACCESS;
+
+ DWORD oldflags;
+ if (!VirtualProtect(aAddr, aSize, flags, &oldflags))
+ NS_RUNTIMEABORT("can't VirtualProtect()");
+}
+
+size_t
+SharedMemory::SystemPageSize()
+{
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwPageSize;
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/Shmem.cpp b/ipc/glue/Shmem.cpp
new file mode 100644
index 000000000..f0cc3bf39
--- /dev/null
+++ b/ipc/glue/Shmem.cpp
@@ -0,0 +1,497 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "Shmem.h"
+
+#include "ProtocolUtils.h"
+#include "SharedMemoryBasic.h"
+
+#include "mozilla/Unused.h"
+
+
+namespace mozilla {
+namespace ipc {
+
+class ShmemCreated : public IPC::Message
+{
+private:
+ typedef Shmem::id_t id_t;
+
+public:
+ ShmemCreated(int32_t routingId,
+ id_t aIPDLId,
+ size_t aSize,
+ SharedMemory::SharedMemoryType aType) :
+ IPC::Message(routingId, SHMEM_CREATED_MESSAGE_TYPE, NESTED_INSIDE_CPOW)
+ {
+ IPC::WriteParam(this, aIPDLId);
+ IPC::WriteParam(this, aSize);
+ IPC::WriteParam(this, int32_t(aType));
+ }
+
+ static bool
+ ReadInfo(const Message* msg, PickleIterator* iter,
+ id_t* aIPDLId,
+ size_t* aSize,
+ SharedMemory::SharedMemoryType* aType)
+ {
+ if (!IPC::ReadParam(msg, iter, aIPDLId) ||
+ !IPC::ReadParam(msg, iter, aSize) ||
+ !IPC::ReadParam(msg, iter, reinterpret_cast<int32_t*>(aType)))
+ return false;
+ return true;
+ }
+
+ void Log(const std::string& aPrefix,
+ FILE* aOutf) const
+ {
+ fputs("(special ShmemCreated msg)", aOutf);
+ }
+};
+
+class ShmemDestroyed : public IPC::Message
+{
+private:
+ typedef Shmem::id_t id_t;
+
+public:
+ ShmemDestroyed(int32_t routingId,
+ id_t aIPDLId) :
+ IPC::Message(routingId, SHMEM_DESTROYED_MESSAGE_TYPE)
+ {
+ IPC::WriteParam(this, aIPDLId);
+ }
+};
+
+static SharedMemory*
+NewSegment(SharedMemory::SharedMemoryType aType)
+{
+ if (SharedMemory::TYPE_BASIC == aType) {
+ return new SharedMemoryBasic;
+ } else {
+ NS_ERROR("unknown Shmem type");
+ return nullptr;
+ }
+}
+
+static already_AddRefed<SharedMemory>
+CreateSegment(SharedMemory::SharedMemoryType aType, size_t aNBytes, size_t aExtraSize)
+{
+ RefPtr<SharedMemory> segment = NewSegment(aType);
+ if (!segment) {
+ return nullptr;
+ }
+ size_t size = SharedMemory::PageAlignedSize(aNBytes + aExtraSize);
+ if (!segment->Create(size) || !segment->Map(size)) {
+ return nullptr;
+ }
+ return segment.forget();
+}
+
+static already_AddRefed<SharedMemory>
+ReadSegment(const IPC::Message& aDescriptor, Shmem::id_t* aId, size_t* aNBytes, size_t aExtraSize)
+{
+ if (SHMEM_CREATED_MESSAGE_TYPE != aDescriptor.type()) {
+ NS_ERROR("expected 'shmem created' message");
+ return nullptr;
+ }
+ SharedMemory::SharedMemoryType type;
+ PickleIterator iter(aDescriptor);
+ if (!ShmemCreated::ReadInfo(&aDescriptor, &iter, aId, aNBytes, &type)) {
+ return nullptr;
+ }
+ RefPtr<SharedMemory> segment = NewSegment(type);
+ if (!segment) {
+ return nullptr;
+ }
+ if (!segment->ReadHandle(&aDescriptor, &iter)) {
+ NS_ERROR("trying to open invalid handle");
+ return nullptr;
+ }
+ aDescriptor.EndRead(iter);
+ size_t size = SharedMemory::PageAlignedSize(*aNBytes + aExtraSize);
+ if (!segment->Map(size)) {
+ return nullptr;
+ }
+ // close the handle to the segment after it is mapped
+ segment->CloseHandle();
+ return segment.forget();
+}
+
+static void
+DestroySegment(SharedMemory* aSegment)
+{
+ // the SharedMemory dtor closes and unmaps the actual OS shmem segment
+ if (aSegment) {
+ aSegment->Release();
+ }
+}
+
+
+#if defined(DEBUG)
+
+static const char sMagic[] =
+ "This little piggy went to market.\n"
+ "This little piggy stayed at home.\n"
+ "This little piggy has roast beef,\n"
+ "This little piggy had none.\n"
+ "And this little piggy cried \"Wee! Wee! Wee!\" all the way home";
+
+
+struct Header {
+ // Don't use size_t or bool here because their size depends on the
+ // architecture.
+ uint32_t mSize;
+ uint32_t mUnsafe;
+ char mMagic[sizeof(sMagic)];
+};
+
+static void
+GetSections(Shmem::SharedMemory* aSegment,
+ Header** aHeader,
+ char** aFrontSentinel,
+ char** aData,
+ char** aBackSentinel)
+{
+ MOZ_ASSERT(aSegment && aFrontSentinel && aData && aBackSentinel,
+ "null param(s)");
+
+ *aFrontSentinel = reinterpret_cast<char*>(aSegment->memory());
+ MOZ_ASSERT(*aFrontSentinel, "null memory()");
+
+ *aHeader = reinterpret_cast<Header*>(*aFrontSentinel);
+
+ size_t pageSize = Shmem::SharedMemory::SystemPageSize();
+ *aData = *aFrontSentinel + pageSize;
+
+ *aBackSentinel = *aFrontSentinel + aSegment->Size() - pageSize;
+}
+
+static Header*
+GetHeader(Shmem::SharedMemory* aSegment)
+{
+ Header* header;
+ char* dontcare;
+ GetSections(aSegment, &header, &dontcare, &dontcare, &dontcare);
+ return header;
+}
+
+static void
+Protect(SharedMemory* aSegment)
+{
+ MOZ_ASSERT(aSegment, "null segment");
+ aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
+ aSegment->Size(),
+ RightsNone);
+}
+
+static void
+Unprotect(SharedMemory* aSegment)
+{
+ MOZ_ASSERT(aSegment, "null segment");
+ aSegment->Protect(reinterpret_cast<char*>(aSegment->memory()),
+ aSegment->Size(),
+ RightsRead | RightsWrite);
+}
+
+//
+// In debug builds, we specially allocate shmem segments. The layout
+// is as follows
+//
+// Page 0: "front sentinel"
+// size of mapping
+// magic bytes
+// Page 1 through n-1:
+// user data
+// Page n: "back sentinel"
+// [nothing]
+//
+// The mapping can be in one of the following states, wrt to the
+// current process.
+//
+// State "unmapped": all pages are mapped with no access rights.
+//
+// State "mapping": all pages are mapped with read/write access.
+//
+// State "mapped": the front and back sentinels are mapped with no
+// access rights, and all the other pages are mapped with
+// read/write access.
+//
+// When a SharedMemory segment is first allocated, it starts out in
+// the "mapping" state for the process that allocates the segment, and
+// in the "unmapped" state for the other process. The allocating
+// process will then create a Shmem, which takes the segment into the
+// "mapped" state, where it can be accessed by clients.
+//
+// When a Shmem is sent to another process in an IPDL message, the
+// segment transitions into the "unmapped" state for the sending
+// process, and into the "mapping" state for the receiving process.
+// The receiving process will then create a Shmem from the underlying
+// segment, and take the segment into the "mapped" state.
+//
+// In the "mapping" state, we use the front sentinel to verify the
+// integrity of the shmem segment. If valid, it has a size_t
+// containing the number of bytes the user allocated followed by the
+// magic bytes above.
+//
+// In the "mapped" state, the front and back sentinels have no access
+// rights. They act as guards against buffer overflows and underflows
+// in client code; if clients touch a sentinel, they die with SIGSEGV.
+//
+// The "unmapped" state is used to enforce single-owner semantics of
+// the shmem segment. If a process other than the current owner tries
+// to touch the segment, it dies with SIGSEGV.
+//
+
+Shmem::Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ SharedMemory* aSegment, id_t aId) :
+ mSegment(aSegment),
+ mData(nullptr),
+ mSize(0)
+{
+ MOZ_ASSERT(mSegment, "null segment");
+ MOZ_ASSERT(aId != 0, "invalid ID");
+
+ Unprotect(mSegment);
+
+ Header* header;
+ char* frontSentinel;
+ char* data;
+ char* backSentinel;
+ GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
+
+ // do a quick validity check to avoid weird-looking crashes in libc
+ char check = *frontSentinel;
+ (void)check;
+
+ MOZ_ASSERT(!strncmp(header->mMagic, sMagic, sizeof(sMagic)),
+ "invalid segment");
+ mSize = static_cast<size_t>(header->mSize);
+
+ size_t pageSize = SharedMemory::SystemPageSize();
+ // transition into the "mapped" state by protecting the front and
+ // back sentinels (which guard against buffer under/overflows)
+ mSegment->Protect(frontSentinel, pageSize, RightsNone);
+ mSegment->Protect(backSentinel, pageSize, RightsNone);
+
+ // don't set these until we know they're valid
+ mData = data;
+ mId = aId;
+}
+
+void
+Shmem::AssertInvariants() const
+{
+ MOZ_ASSERT(mSegment, "null segment");
+ MOZ_ASSERT(mData, "null data pointer");
+ MOZ_ASSERT(mSize > 0, "invalid size");
+ // if the segment isn't owned by the current process, these will
+ // trigger SIGSEGV
+ char checkMappingFront = *reinterpret_cast<char*>(mData);
+ char checkMappingBack = *(reinterpret_cast<char*>(mData) + mSize - 1);
+
+ // avoid "unused" warnings for these variables:
+ Unused << checkMappingFront;
+ Unused << checkMappingBack;
+}
+
+void
+Shmem::RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
+{
+ AssertInvariants();
+
+ size_t pageSize = SharedMemory::SystemPageSize();
+ Header* header = GetHeader(mSegment);
+
+ // Open this up for reading temporarily
+ mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsRead);
+
+ if (!header->mUnsafe) {
+ Protect(mSegment);
+ } else {
+ mSegment->Protect(reinterpret_cast<char*>(header), pageSize, RightsNone);
+ }
+}
+
+// static
+already_AddRefed<Shmem::SharedMemory>
+Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ size_t aNBytes,
+ SharedMemoryType aType,
+ bool aUnsafe,
+ bool aProtect)
+{
+ NS_ASSERTION(aNBytes <= UINT32_MAX, "Will truncate shmem segment size!");
+ MOZ_ASSERT(!aProtect || !aUnsafe, "protect => !unsafe");
+
+ size_t pageSize = SharedMemory::SystemPageSize();
+ // |2*pageSize| is for the front and back sentinel
+ RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, 2*pageSize);
+ if (!segment) {
+ return nullptr;
+ }
+
+ Header* header;
+ char *frontSentinel;
+ char *data;
+ char *backSentinel;
+ GetSections(segment, &header, &frontSentinel, &data, &backSentinel);
+
+ // initialize the segment with Shmem-internal information
+
+ // NB: this can't be a static assert because technically pageSize
+ // isn't known at compile time, event though in practice it's always
+ // going to be 4KiB
+ MOZ_ASSERT(sizeof(Header) <= pageSize,
+ "Shmem::Header has gotten too big");
+ memcpy(header->mMagic, sMagic, sizeof(sMagic));
+ header->mSize = static_cast<uint32_t>(aNBytes);
+ header->mUnsafe = aUnsafe;
+
+ if (aProtect)
+ Protect(segment);
+
+ return segment.forget();
+}
+
+// static
+already_AddRefed<Shmem::SharedMemory>
+Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ const IPC::Message& aDescriptor,
+ id_t* aId,
+ bool aProtect)
+{
+ size_t size;
+ size_t pageSize = SharedMemory::SystemPageSize();
+ // |2*pageSize| is for the front and back sentinels
+ RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size, 2*pageSize);
+ if (!segment) {
+ return nullptr;
+ }
+
+ Header* header = GetHeader(segment);
+
+ if (size != header->mSize) {
+ // Deallocation should zero out the header, so check for that.
+ if (header->mSize || header->mUnsafe || header->mMagic[0] ||
+ memcmp(header->mMagic, &header->mMagic[1], sizeof(header->mMagic)-1)) {
+ NS_ERROR("Wrong size for this Shmem!");
+ } else {
+ NS_WARNING("Shmem was deallocated");
+ }
+ return nullptr;
+ }
+
+ // The caller of this function may not know whether the segment is
+ // unsafe or not
+ if (!header->mUnsafe && aProtect)
+ Protect(segment);
+
+ return segment.forget();
+}
+
+// static
+void
+Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ SharedMemory* aSegment)
+{
+ if (!aSegment)
+ return;
+
+ size_t pageSize = SharedMemory::SystemPageSize();
+ Header* header;
+ char *frontSentinel;
+ char *data;
+ char *backSentinel;
+ GetSections(aSegment, &header, &frontSentinel, &data, &backSentinel);
+
+ aSegment->Protect(frontSentinel, pageSize, RightsWrite | RightsRead);
+ memset(header->mMagic, 0, sizeof(sMagic));
+ header->mSize = 0;
+ header->mUnsafe = false; // make it "safe" so as to catch errors
+
+ DestroySegment(aSegment);
+}
+
+
+#else // !defined(DEBUG)
+
+// static
+already_AddRefed<Shmem::SharedMemory>
+Shmem::Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ size_t aNBytes,
+ SharedMemoryType aType,
+ bool /*unused*/,
+ bool /*unused*/)
+{
+ RefPtr<SharedMemory> segment = CreateSegment(aType, aNBytes, sizeof(uint32_t));
+ if (!segment) {
+ return nullptr;
+ }
+
+ *PtrToSize(segment) = static_cast<uint32_t>(aNBytes);
+
+ return segment.forget();
+}
+
+// static
+already_AddRefed<Shmem::SharedMemory>
+Shmem::OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ const IPC::Message& aDescriptor,
+ id_t* aId,
+ bool /*unused*/)
+{
+ size_t size;
+ RefPtr<SharedMemory> segment = ReadSegment(aDescriptor, aId, &size, sizeof(uint32_t));
+ if (!segment) {
+ return nullptr;
+ }
+
+ // this is the only validity check done in non-DEBUG builds
+ if (size != static_cast<size_t>(*PtrToSize(segment))) {
+ return nullptr;
+ }
+
+ return segment.forget();
+}
+
+// static
+void
+Shmem::Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ SharedMemory* aSegment)
+{
+ DestroySegment(aSegment);
+}
+
+#endif // if defined(DEBUG)
+
+IPC::Message*
+Shmem::ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ base::ProcessId aTargetPid,
+ int32_t routingId)
+{
+ AssertInvariants();
+
+ IPC::Message *msg = new ShmemCreated(routingId, mId, mSize, mSegment->Type());
+ if (!mSegment->ShareHandle(aTargetPid, msg)) {
+ return nullptr;
+ }
+ // close the handle to the segment after it is shared
+ mSegment->CloseHandle();
+ return msg;
+}
+
+IPC::Message*
+Shmem::UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ base::ProcessId aTargetPid,
+ int32_t routingId)
+{
+ AssertInvariants();
+ return new ShmemDestroyed(routingId, mId);
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/Shmem.h b/ipc/glue/Shmem.h
new file mode 100644
index 000000000..2736c9ac1
--- /dev/null
+++ b/ipc/glue/Shmem.h
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_Shmem_h
+#define mozilla_ipc_Shmem_h
+
+#include "mozilla/Attributes.h"
+
+#include "base/basictypes.h"
+#include "base/process.h"
+
+#include "nscore.h"
+#include "nsDebug.h"
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/ipc/SharedMemory.h"
+
+/**
+ * |Shmem| is one agent in the IPDL shared memory scheme. The way it
+ works is essentially
+ *
+ * (1) C++ code calls, say, |parentActor->AllocShmem(size)|
+
+ * (2) IPDL-generated code creates a |mozilla::ipc::SharedMemory|
+ * wrapping the bare OS shmem primitives. The code then adds the new
+ * SharedMemory to the set of shmem segments being managed by IPDL.
+ *
+ * (3) IPDL-generated code "shares" the new SharedMemory to the child
+ * process, and then sends a special asynchronous IPC message to the
+ * child notifying it of the creation of the segment. (What this
+ * means is OS specific.)
+ *
+ * (4a) The child receives the special IPC message, and using the
+ * |SharedMemory{Basic}::Handle| it was passed, creates a
+ * |mozilla::ipc::SharedMemory| in the child
+ * process.
+ *
+ * (4b) After sending the "shmem-created" IPC message, IPDL-generated
+ * code in the parent returns a |mozilla::ipc::Shmem| back to the C++
+ * caller of |parentActor->AllocShmem()|. The |Shmem| is a "weak
+ * reference" to the underlying |SharedMemory|, which is managed by
+ * IPDL-generated code. C++ consumers of |Shmem| can't get at the
+ * underlying |SharedMemory|.
+ *
+ * If parent code wants to give access rights to the Shmem to the
+ * child, it does so by sending its |Shmem| to the child, in an IPDL
+ * message. The parent's |Shmem| then "dies", i.e. becomes
+ * inaccessible. This process could be compared to passing a
+ * "shmem-access baton" between parent and child.
+ */
+
+namespace mozilla {
+namespace layers {
+class ShadowLayerForwarder;
+} // namespace layers
+
+namespace ipc {
+
+class Shmem final
+{
+ friend struct IPC::ParamTraits<mozilla::ipc::Shmem>;
+#ifdef DEBUG
+ // For ShadowLayerForwarder::CheckSurfaceDescriptor
+ friend class mozilla::layers::ShadowLayerForwarder;
+#endif
+
+public:
+ typedef int32_t id_t;
+ // Low-level wrapper around platform shmem primitives.
+ typedef mozilla::ipc::SharedMemory SharedMemory;
+ typedef SharedMemory::SharedMemoryType SharedMemoryType;
+ struct IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead {};
+
+ Shmem() :
+ mSegment(nullptr),
+ mData(nullptr),
+ mSize(0),
+ mId(0)
+ {
+ }
+
+ Shmem(const Shmem& aOther) :
+ mSegment(aOther.mSegment),
+ mData(aOther.mData),
+ mSize(aOther.mSize),
+ mId(aOther.mId)
+ {
+ }
+
+#if !defined(DEBUG)
+ Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ SharedMemory* aSegment, id_t aId) :
+ mSegment(aSegment),
+ mData(aSegment->memory()),
+ mSize(0),
+ mId(aId)
+ {
+ mSize = static_cast<size_t>(*PtrToSize(mSegment));
+ }
+#else
+ Shmem(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ SharedMemory* aSegment, id_t aId);
+#endif
+
+ ~Shmem()
+ {
+ // Shmem only holds a "weak ref" to the actual segment, which is
+ // owned by IPDL. So there's nothing interesting to be done here
+ forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead());
+ }
+
+ Shmem& operator=(const Shmem& aRhs)
+ {
+ mSegment = aRhs.mSegment;
+ mData = aRhs.mData;
+ mSize = aRhs.mSize;
+ mId = aRhs.mId;
+ return *this;
+ }
+
+ bool operator==(const Shmem& aRhs) const
+ {
+ return mSegment == aRhs.mSegment;
+ }
+
+ // Returns whether this Shmem is writable by you, and thus whether you can
+ // transfer writability to another actor.
+ bool
+ IsWritable() const
+ {
+ return mSegment != nullptr;
+ }
+
+ // Returns whether this Shmem is readable by you, and thus whether you can
+ // transfer readability to another actor.
+ bool
+ IsReadable() const
+ {
+ return mSegment != nullptr;
+ }
+
+ // Return a pointer to the user-visible data segment.
+ template<typename T>
+ T*
+ get() const
+ {
+ AssertInvariants();
+ AssertAligned<T>();
+
+ return reinterpret_cast<T*>(mData);
+ }
+
+ // Return the size of the segment as requested when this shmem
+ // segment was allocated, in units of T. The underlying mapping may
+ // actually be larger because of page alignment and private data,
+ // but this isn't exposed to clients.
+ template<typename T>
+ size_t
+ Size() const
+ {
+ AssertInvariants();
+ AssertAligned<T>();
+
+ return mSize / sizeof(T);
+ }
+
+ // These shouldn't be used directly, use the IPDL interface instead.
+ id_t Id(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
+ return mId;
+ }
+
+ SharedMemory* Segment(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead) const {
+ return mSegment;
+ }
+
+#ifndef DEBUG
+ void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
+ {
+ }
+#else
+ void RevokeRights(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead);
+#endif
+
+ void forget(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead)
+ {
+ mSegment = nullptr;
+ mData = nullptr;
+ mSize = 0;
+ mId = 0;
+ }
+
+ static already_AddRefed<Shmem::SharedMemory>
+ Alloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ size_t aNBytes,
+ SharedMemoryType aType,
+ bool aUnsafe,
+ bool aProtect=false);
+
+ // Prepare this to be shared with |aProcess|. Return an IPC message
+ // that contains enough information for the other process to map
+ // this segment in OpenExisting() below. Return a new message if
+ // successful (owned by the caller), nullptr if not.
+ IPC::Message*
+ ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ base::ProcessId aTargetPid,
+ int32_t routingId);
+
+ // Stop sharing this with |aTargetPid|. Return an IPC message that
+ // contains enough information for the other process to unmap this
+ // segment. Return a new message if successful (owned by the
+ // caller), nullptr if not.
+ IPC::Message*
+ UnshareFrom(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ base::ProcessId aTargetPid,
+ int32_t routingId);
+
+ // Return a SharedMemory instance in this process using the
+ // descriptor shared to us by the process that created the
+ // underlying OS shmem resource. The contents of the descriptor
+ // depend on the type of SharedMemory that was passed to us.
+ static already_AddRefed<SharedMemory>
+ OpenExisting(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ const IPC::Message& aDescriptor,
+ id_t* aId,
+ bool aProtect=false);
+
+ static void
+ Dealloc(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
+ SharedMemory* aSegment);
+
+private:
+ template<typename T>
+ void AssertAligned() const
+ {
+ if (0 != (mSize % sizeof(T)))
+ NS_RUNTIMEABORT("shmem is not T-aligned");
+ }
+
+#if !defined(DEBUG)
+ void AssertInvariants() const
+ { }
+
+ static uint32_t*
+ PtrToSize(SharedMemory* aSegment)
+ {
+ char* endOfSegment =
+ reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size();
+ return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t));
+ }
+
+#else
+ void AssertInvariants() const;
+#endif
+
+ RefPtr<SharedMemory> mSegment;
+ void* mData;
+ size_t mSize;
+ id_t mId;
+};
+
+
+} // namespace ipc
+} // namespace mozilla
+
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::ipc::Shmem>
+{
+ typedef mozilla::ipc::Shmem paramType;
+
+ // NB: Read()/Write() look creepy in that Shmems have a pointer
+ // member, but IPDL internally uses mId to properly initialize a
+ // "real" Shmem
+
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mId);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ paramType::id_t id;
+ if (!ReadParam(aMsg, aIter, &id))
+ return false;
+ aResult->mId = id;
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {
+ aLog->append(L"(shmem segment)");
+ }
+};
+
+
+} // namespace IPC
+
+
+#endif // ifndef mozilla_ipc_Shmem_h
diff --git a/ipc/glue/StringUtil.cpp b/ipc/glue/StringUtil.cpp
new file mode 100644
index 000000000..17e4edcd2
--- /dev/null
+++ b/ipc/glue/StringUtil.cpp
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/string_util.h"
+
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/sys_string_conversions.h"
+
+#include "base/string_piece.h"
+#include "base/string_util.h"
+
+#include "build/build_config.h"
+
+// FIXME/cjones: these really only pertain to the linux sys string
+// converters.
+#ifdef WCHAR_T_IS_UTF16
+# define ICONV_WCHAR_T_ENCODING "UTF-16"
+#else
+# define ICONV_WCHAR_T_ENCODING "WCHAR_T"
+#endif
+
+// FIXME/cjones: BIG assumption here that std::string is a good
+// container of UTF8-encoded strings. this is probably wrong, as its
+// API doesn't really make sense for UTF8.
+
+namespace base {
+
+// FIXME/cjones: as its name implies, this function is a hack.
+template<typename FromType, typename ToType>
+ToType
+GhettoStringConvert(const FromType& in)
+{
+ // FIXME/cjones: assumes no non-ASCII characters in |in|
+ ToType out;
+ out.resize(in.length());
+ for (int i = 0; i < static_cast<int>(in.length()); ++i)
+ out[i] = static_cast<typename ToType::value_type>(in[i]);
+ return out;
+}
+
+} // namespace base
+
+// Implement functions that were in the chromium ICU library, which
+// we're not taking.
+
+std::string
+WideToUTF8(const std::wstring& wide)
+{
+ return base::SysWideToUTF8(wide);
+}
+
+std::wstring
+UTF8ToWide(const StringPiece& utf8)
+{
+ return base::SysUTF8ToWide(utf8);
+}
+
+namespace base {
+
+// FIXME/cjones: here we're entirely replacing the linux string
+// converters, and implementing the one that doesn't exist for OS X
+// and Windows.
+
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
+std::string SysWideToUTF8(const std::wstring& wide) {
+ // FIXME/cjones: do this with iconv
+ return GhettoStringConvert<std::wstring, std::string>(wide);
+}
+#endif
+
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
+std::wstring SysUTF8ToWide(const StringPiece& utf8) {
+ // FIXME/cjones: do this with iconv
+ return GhettoStringConvert<StringPiece, std::wstring>(utf8);
+}
+
+std::string SysWideToNativeMB(const std::wstring& wide) {
+ // TODO(evanm): we can't assume Linux is UTF-8.
+ return SysWideToUTF8(wide);
+}
+
+std::wstring SysNativeMBToWide(const StringPiece& native_mb) {
+ // TODO(evanm): we can't assume Linux is UTF-8.
+ return SysUTF8ToWide(native_mb);
+}
+#endif
+
+} // namespace base
diff --git a/ipc/glue/TaskFactory.h b/ipc/glue/TaskFactory.h
new file mode 100644
index 000000000..66d9d58cd
--- /dev/null
+++ b/ipc/glue/TaskFactory.h
@@ -0,0 +1,110 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_plugins_TaskFactory_h
+#define mozilla_plugins_TaskFactory_h
+
+#include <base/task.h>
+
+#include "mozilla/Move.h"
+
+/*
+ * This is based on the ScopedRunnableMethodFactory from ipc/chromium/src/base/task.h
+ * Chromium's factories assert if tasks are created and run on different threads,
+ * which is something we need to do in PluginModuleParent (hang UI vs. main thread).
+ * TaskFactory just provides cancellable tasks that don't assert this.
+ * This version also allows both ScopedMethod and regular Tasks to be generated
+ * by the same Factory object.
+ */
+
+namespace mozilla {
+namespace ipc {
+
+template<class T>
+class TaskFactory : public RevocableStore
+{
+private:
+ template<class TaskType>
+ class TaskWrapper : public TaskType
+ {
+ public:
+ template<typename... Args>
+ explicit TaskWrapper(RevocableStore* store, Args&&... args)
+ : TaskType(mozilla::Forward<Args>(args)...)
+ , revocable_(store)
+ {
+ }
+
+ NS_IMETHOD Run() override {
+ if (!revocable_.revoked())
+ TaskType::Run();
+ return NS_OK;
+ }
+
+ private:
+ Revocable revocable_;
+ };
+
+public:
+ explicit TaskFactory(T* object) : object_(object) { }
+
+ template <typename TaskParamType, typename... Args>
+ inline already_AddRefed<TaskParamType> NewTask(Args&&... args)
+ {
+ typedef TaskWrapper<TaskParamType> TaskWrapper;
+ RefPtr<TaskWrapper> task =
+ new TaskWrapper(this, mozilla::Forward<Args>(args)...);
+ return task.forget();
+ }
+
+ template <class Method>
+ inline already_AddRefed<Runnable> NewRunnableMethod(Method method) {
+ typedef TaskWrapper<RunnableMethod<Method, Tuple0> > TaskWrapper;
+
+ RefPtr<TaskWrapper> task = new TaskWrapper(this, object_, method,
+ base::MakeTuple());
+
+ return task.forget();
+ }
+
+ template <class Method, class A>
+ inline already_AddRefed<Runnable> NewRunnableMethod(Method method, const A& a) {
+ typedef TaskWrapper<RunnableMethod<Method, Tuple1<A> > > TaskWrapper;
+
+ RefPtr<TaskWrapper> task = new TaskWrapper(this, object_, method,
+ base::MakeTuple(a));
+
+ return task.forget();
+ }
+
+protected:
+ template <class Method, class Params>
+ class RunnableMethod : public Runnable {
+ public:
+ RunnableMethod(T* obj, Method meth, const Params& params)
+ : obj_(obj)
+ , meth_(meth)
+ , params_(params) {
+
+ }
+
+ NS_IMETHOD Run() override {
+ DispatchToMethod(obj_, meth_, params_);
+ return NS_OK;
+ }
+
+ private:
+ T* obj_;
+ Method meth_;
+ Params params_;
+ };
+
+private:
+ T* object_;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_plugins_TaskFactory_h
diff --git a/ipc/glue/Transport.h b/ipc/glue/Transport.h
new file mode 100644
index 000000000..699a22260
--- /dev/null
+++ b/ipc/glue/Transport.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_ipc_Transport_h
+#define mozilla_ipc_Transport_h 1
+
+#include "base/process_util.h"
+#include "chrome/common/ipc_channel.h"
+
+#ifdef OS_POSIX
+# include "mozilla/ipc/Transport_posix.h"
+#elif OS_WIN
+# include "mozilla/ipc/Transport_win.h"
+#endif
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace ipc {
+
+class FileDescriptor;
+
+typedef IPC::Channel Transport;
+
+nsresult CreateTransport(base::ProcessId aProcIdOne,
+ TransportDescriptor* aOne,
+ TransportDescriptor* aTwo);
+
+UniquePtr<Transport> OpenDescriptor(const TransportDescriptor& aTd,
+ Transport::Mode aMode);
+
+UniquePtr<Transport> OpenDescriptor(const FileDescriptor& aFd,
+ Transport::Mode aMode);
+
+TransportDescriptor
+DuplicateDescriptor(const TransportDescriptor& aTd);
+
+void CloseDescriptor(const TransportDescriptor& aTd);
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_Transport_h
diff --git a/ipc/glue/Transport_posix.cpp b/ipc/glue/Transport_posix.cpp
new file mode 100644
index 000000000..a8ac08464
--- /dev/null
+++ b/ipc/glue/Transport_posix.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <unistd.h>
+
+#include <string>
+
+#include "base/eintr_wrapper.h"
+
+#include "mozilla/ipc/Transport.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "ProtocolUtils.h"
+
+using namespace std;
+
+using base::ProcessHandle;
+
+namespace mozilla {
+namespace ipc {
+
+nsresult
+CreateTransport(base::ProcessId aProcIdOne,
+ TransportDescriptor* aOne,
+ TransportDescriptor* aTwo)
+{
+ wstring id = IPC::Channel::GenerateVerifiedChannelID(std::wstring());
+ // Use MODE_SERVER to force creation of the socketpair
+ Transport t(id, Transport::MODE_SERVER, nullptr);
+ int fd1 = t.GetFileDescriptor();
+ int fd2, dontcare;
+ t.GetClientFileDescriptorMapping(&fd2, &dontcare);
+ if (fd1 < 0 || fd2 < 0) {
+ return NS_ERROR_TRANSPORT_INIT;
+ }
+
+ // The Transport closes these fds when it goes out of scope, so we
+ // dup them here
+ fd1 = dup(fd1);
+ if (fd1 < 0) {
+ AnnotateCrashReportWithErrno("IpcCreateTransportDupErrno", errno);
+ }
+ fd2 = dup(fd2);
+ if (fd2 < 0) {
+ AnnotateCrashReportWithErrno("IpcCreateTransportDupErrno", errno);
+ }
+
+ if (fd1 < 0 || fd2 < 0) {
+ HANDLE_EINTR(close(fd1));
+ HANDLE_EINTR(close(fd2));
+ return NS_ERROR_DUPLICATE_HANDLE;
+ }
+
+ aOne->mFd = base::FileDescriptor(fd1, true/*close after sending*/);
+ aTwo->mFd = base::FileDescriptor(fd2, true/*close after sending*/);
+ return NS_OK;
+}
+
+UniquePtr<Transport>
+OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode)
+{
+ return MakeUnique<Transport>(aTd.mFd.fd, aMode, nullptr);
+}
+
+UniquePtr<Transport>
+OpenDescriptor(const FileDescriptor& aFd, Transport::Mode aMode)
+{
+ auto rawFD = aFd.ClonePlatformHandle();
+ return MakeUnique<Transport>(rawFD.release(), aMode, nullptr);
+}
+
+TransportDescriptor
+DuplicateDescriptor(const TransportDescriptor& aTd)
+{
+ TransportDescriptor result = aTd;
+ result.mFd.fd = dup(aTd.mFd.fd);
+ if (result.mFd.fd == -1) {
+ AnnotateSystemError();
+ }
+ MOZ_RELEASE_ASSERT(result.mFd.fd != -1, "DuplicateDescriptor failed");
+ return result;
+}
+
+void
+CloseDescriptor(const TransportDescriptor& aTd)
+{
+ close(aTd.mFd.fd);
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/Transport_posix.h b/ipc/glue/Transport_posix.h
new file mode 100644
index 000000000..fc2c89aaa
--- /dev/null
+++ b/ipc/glue/Transport_posix.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_Transport_posix_h
+#define mozilla_ipc_Transport_posix_h 1
+
+#include "ipc/IPCMessageUtils.h"
+
+
+namespace mozilla {
+namespace ipc {
+
+struct TransportDescriptor
+{
+ base::FileDescriptor mFd;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::ipc::TransportDescriptor>
+{
+ typedef mozilla::ipc::TransportDescriptor paramType;
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ WriteParam(aMsg, aParam.mFd);
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ return ReadParam(aMsg, aIter, &aResult->mFd);
+ }
+};
+
+} // namespace IPC
+
+
+#endif // mozilla_ipc_Transport_posix_h
diff --git a/ipc/glue/Transport_win.cpp b/ipc/glue/Transport_win.cpp
new file mode 100644
index 000000000..28e9026c3
--- /dev/null
+++ b/ipc/glue/Transport_win.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/message_loop.h"
+
+#include "mozilla/ipc/Transport.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+using namespace std;
+
+using base::ProcessHandle;
+
+namespace mozilla {
+namespace ipc {
+
+nsresult
+CreateTransport(base::ProcessId aProcIdOne,
+ TransportDescriptor* aOne,
+ TransportDescriptor* aTwo)
+{
+ wstring id = IPC::Channel::GenerateVerifiedChannelID(std::wstring());
+ // Use MODE_SERVER to force creation of the pipe
+ Transport t(id, Transport::MODE_SERVER, nullptr);
+ HANDLE serverPipe = t.GetServerPipeHandle();
+ if (!serverPipe) {
+ return NS_ERROR_TRANSPORT_INIT;
+ }
+
+ // NB: we create the server pipe immediately, instead of just
+ // grabbing an ID, on purpose. In the current setup, the client
+ // needs to connect to an existing server pipe, so to prevent race
+ // conditions, we create the server side here. When we send the pipe
+ // to the server, we DuplicateHandle it to the server process to give it
+ // access.
+ HANDLE serverDup;
+ DWORD access = 0;
+ DWORD options = DUPLICATE_SAME_ACCESS;
+ if (!DuplicateHandle(serverPipe, base::GetCurrentProcId(), &serverDup, access, options)) {
+ return NS_ERROR_DUPLICATE_HANDLE;
+ }
+
+ aOne->mPipeName = aTwo->mPipeName = id;
+ aOne->mServerPipeHandle = serverDup;
+ aOne->mDestinationProcessId = aProcIdOne;
+ aTwo->mServerPipeHandle = INVALID_HANDLE_VALUE;
+ aTwo->mDestinationProcessId = 0;
+ return NS_OK;
+}
+
+HANDLE
+TransferHandleToProcess(HANDLE source, base::ProcessId pid)
+{
+ // At this point we're sending the handle to another process.
+
+ if (source == INVALID_HANDLE_VALUE) {
+ return source;
+ }
+ HANDLE handleDup;
+ DWORD access = 0;
+ DWORD options = DUPLICATE_SAME_ACCESS;
+ bool ok = DuplicateHandle(source, pid, &handleDup, access, options);
+ if (!ok) {
+ return nullptr;
+ }
+
+ // Now close our own copy of the handle (we're supposed to be transferring,
+ // not copying).
+ CloseHandle(source);
+
+ return handleDup;
+}
+
+UniquePtr<Transport>
+OpenDescriptor(const TransportDescriptor& aTd, Transport::Mode aMode)
+{
+ if (aTd.mServerPipeHandle != INVALID_HANDLE_VALUE) {
+ MOZ_RELEASE_ASSERT(aTd.mDestinationProcessId == base::GetCurrentProcId());
+ }
+ return MakeUnique<Transport>(aTd.mPipeName, aTd.mServerPipeHandle, aMode, nullptr);
+}
+
+UniquePtr<Transport>
+OpenDescriptor(const FileDescriptor& aFd, Transport::Mode aMode)
+{
+ NS_NOTREACHED("Not implemented!");
+ return nullptr;
+}
+
+TransportDescriptor
+DuplicateDescriptor(const TransportDescriptor& aTd)
+{
+ // We're duplicating this handle in our own process for bookkeeping purposes.
+
+ if (aTd.mServerPipeHandle == INVALID_HANDLE_VALUE) {
+ return aTd;
+ }
+
+ HANDLE serverDup;
+ DWORD access = 0;
+ DWORD options = DUPLICATE_SAME_ACCESS;
+ bool ok = DuplicateHandle(aTd.mServerPipeHandle, base::GetCurrentProcId(),
+ &serverDup, access, options);
+ if (!ok) {
+ AnnotateSystemError();
+ }
+ MOZ_RELEASE_ASSERT(ok);
+
+ TransportDescriptor desc = aTd;
+ desc.mServerPipeHandle = serverDup;
+ return desc;
+}
+
+void
+CloseDescriptor(const TransportDescriptor& aTd)
+{
+ // We're closing our own local copy of the pipe.
+
+ CloseHandle(aTd.mServerPipeHandle);
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/Transport_win.h b/ipc/glue/Transport_win.h
new file mode 100644
index 000000000..26aad6b34
--- /dev/null
+++ b/ipc/glue/Transport_win.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_Transport_win_h
+#define mozilla_ipc_Transport_win_h 1
+
+#include <string>
+
+#include "base/process.h"
+#include "ipc/IPCMessageUtils.h"
+#include "nsWindowsHelpers.h"
+
+namespace mozilla {
+namespace ipc {
+
+struct TransportDescriptor
+{
+ std::wstring mPipeName;
+ HANDLE mServerPipeHandle;
+ base::ProcessId mDestinationProcessId;
+};
+
+HANDLE
+TransferHandleToProcess(HANDLE source, base::ProcessId pid);
+
+} // namespace ipc
+} // namespace mozilla
+
+
+namespace IPC {
+
+template<>
+struct ParamTraits<mozilla::ipc::TransportDescriptor>
+{
+ typedef mozilla::ipc::TransportDescriptor paramType;
+ static void Write(Message* aMsg, const paramType& aParam)
+ {
+ HANDLE pipe = mozilla::ipc::TransferHandleToProcess(aParam.mServerPipeHandle,
+ aParam.mDestinationProcessId);
+ DWORD duplicateFromProcessId = 0;
+ if (!pipe) {
+ if (XRE_IsParentProcess()) {
+ // If we are the parent and failed to transfer then there is no hope,
+ // just close the handle.
+ ::CloseHandle(aParam.mServerPipeHandle);
+ } else {
+ // We are probably sending to parent so it should be able to duplicate.
+ pipe = aParam.mServerPipeHandle;
+ duplicateFromProcessId = ::GetCurrentProcessId();
+ }
+ }
+
+ WriteParam(aMsg, aParam.mPipeName);
+ WriteParam(aMsg, pipe);
+ WriteParam(aMsg, duplicateFromProcessId);
+ WriteParam(aMsg, aParam.mDestinationProcessId);
+ }
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ DWORD duplicateFromProcessId;
+ bool r = (ReadParam(aMsg, aIter, &aResult->mPipeName) &&
+ ReadParam(aMsg, aIter, &aResult->mServerPipeHandle) &&
+ ReadParam(aMsg, aIter, &duplicateFromProcessId) &&
+ ReadParam(aMsg, aIter, &aResult->mDestinationProcessId));
+ if (!r) {
+ return r;
+ }
+
+ MOZ_RELEASE_ASSERT(aResult->mServerPipeHandle,
+ "Main process failed to duplicate pipe handle to child.");
+
+ // If this is a not the "server" side descriptor, we have finished.
+ if (aResult->mServerPipeHandle == INVALID_HANDLE_VALUE) {
+ return true;
+ }
+
+ MOZ_RELEASE_ASSERT(aResult->mDestinationProcessId == base::GetCurrentProcId());
+
+ // If the pipe has already been duplicated to us, we have finished.
+ if (!duplicateFromProcessId) {
+ return true;
+ }
+
+ // Otherwise duplicate the handle to us.
+ nsAutoHandle sourceProcess(::OpenProcess(PROCESS_DUP_HANDLE, FALSE,
+ duplicateFromProcessId));
+ if (!sourceProcess) {
+ return false;
+ }
+
+ HANDLE ourHandle;
+ BOOL duped = ::DuplicateHandle(sourceProcess, aResult->mServerPipeHandle,
+ ::GetCurrentProcess(), &ourHandle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+ if (!duped) {
+ aResult->mServerPipeHandle = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ aResult->mServerPipeHandle = ourHandle;
+ return true;
+ }
+};
+
+} // namespace IPC
+
+
+#endif // mozilla_ipc_Transport_win_h
diff --git a/ipc/glue/URIParams.ipdlh b/ipc/glue/URIParams.ipdlh
new file mode 100644
index 000000000..8b8ba3559
--- /dev/null
+++ b/ipc/glue/URIParams.ipdlh
@@ -0,0 +1,112 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+
+include PBackgroundSharedTypes;
+
+namespace mozilla {
+namespace ipc {
+
+struct SimpleURIParams
+{
+ nsCString scheme;
+ nsCString path;
+ nsCString ref;
+ nsCString query;
+ bool isMutable;
+};
+
+struct StandardURLSegment
+{
+ uint32_t position;
+ int32_t length;
+};
+
+struct StandardURLParams
+{
+ uint32_t urlType;
+ int32_t port;
+ int32_t defaultPort;
+ nsCString spec;
+ StandardURLSegment scheme;
+ StandardURLSegment authority;
+ StandardURLSegment username;
+ StandardURLSegment password;
+ StandardURLSegment host;
+ StandardURLSegment path;
+ StandardURLSegment filePath;
+ StandardURLSegment directory;
+ StandardURLSegment baseName;
+ StandardURLSegment extension;
+ StandardURLSegment query;
+ StandardURLSegment ref;
+ nsCString originCharset;
+ bool isMutable;
+ bool supportsFileURL;
+ uint32_t hostEncoding;
+};
+
+struct JARURIParams
+{
+ URIParams jarFile;
+ URIParams jarEntry;
+ nsCString charset;
+};
+
+struct IconURIParams
+{
+ OptionalURIParams uri;
+ uint32_t size;
+ nsCString contentType;
+ nsCString fileName;
+ nsCString stockIcon;
+ int32_t iconSize;
+ int32_t iconState;
+};
+
+struct NullPrincipalURIParams
+{
+ // Purposefully empty. Null principal URIs do not round-trip.
+};
+
+struct HostObjectURIParams
+{
+ SimpleURIParams simpleParams;
+ OptionalPrincipalInfo principal;
+};
+
+union URIParams
+{
+ SimpleURIParams;
+ StandardURLParams;
+ JARURIParams;
+ IconURIParams;
+ NullPrincipalURIParams;
+ JSURIParams;
+ SimpleNestedURIParams;
+ HostObjectURIParams;
+};
+
+union OptionalURIParams
+{
+ void_t;
+ URIParams;
+};
+
+struct JSURIParams
+{
+ SimpleURIParams simpleParams;
+ OptionalURIParams baseURI;
+};
+
+struct SimpleNestedURIParams
+{
+ SimpleURIParams simpleParams;
+ URIParams innerURI;
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/URIUtils.cpp b/ipc/glue/URIUtils.cpp
new file mode 100644
index 000000000..ec7656d74
--- /dev/null
+++ b/ipc/glue/URIUtils.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "URIUtils.h"
+
+#include "nsIIPCSerializableURI.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "nsComponentManagerUtils.h"
+#include "nsDebug.h"
+#include "nsID.h"
+#include "nsJARURI.h"
+#include "nsIIconURI.h"
+#include "nsHostObjectURI.h"
+#include "nsNullPrincipalURI.h"
+#include "nsJSProtocolHandler.h"
+#include "nsNetCID.h"
+#include "nsSimpleNestedURI.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla::ipc;
+using mozilla::ArrayLength;
+
+namespace {
+
+NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
+NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
+NS_DEFINE_CID(kJARURICID, NS_JARURI_CID);
+NS_DEFINE_CID(kIconURICID, NS_MOZICONURI_CID);
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+
+void
+SerializeURI(nsIURI* aURI,
+ URIParams& aParams)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aURI);
+
+ nsCOMPtr<nsIIPCSerializableURI> serializable = do_QueryInterface(aURI);
+ if (!serializable) {
+ MOZ_CRASH("All IPDL URIs must be serializable!");
+ }
+
+ serializable->Serialize(aParams);
+ if (aParams.type() == URIParams::T__None) {
+ MOZ_CRASH("Serialize failed!");
+ }
+}
+
+void
+SerializeURI(nsIURI* aURI,
+ OptionalURIParams& aParams)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (aURI) {
+ URIParams params;
+ SerializeURI(aURI, params);
+ aParams = params;
+ }
+ else {
+ aParams = mozilla::void_t();
+ }
+}
+
+already_AddRefed<nsIURI>
+DeserializeURI(const URIParams& aParams)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIIPCSerializableURI> serializable;
+
+ switch (aParams.type()) {
+ case URIParams::TSimpleURIParams:
+ serializable = do_CreateInstance(kSimpleURICID);
+ break;
+
+ case URIParams::TStandardURLParams:
+ serializable = do_CreateInstance(kStandardURLCID);
+ break;
+
+ case URIParams::TJARURIParams:
+ serializable = do_CreateInstance(kJARURICID);
+ break;
+
+ case URIParams::TJSURIParams:
+ serializable = new nsJSURI();
+ break;
+
+ case URIParams::TIconURIParams:
+ serializable = do_CreateInstance(kIconURICID);
+ break;
+
+ case URIParams::TNullPrincipalURIParams:
+ serializable = new nsNullPrincipalURI();
+ break;
+
+ case URIParams::TSimpleNestedURIParams:
+ serializable = new nsSimpleNestedURI();
+ break;
+
+ case URIParams::THostObjectURIParams:
+ serializable = new nsHostObjectURI();
+ break;
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ MOZ_ASSERT(serializable);
+
+ if (!serializable->Deserialize(aParams)) {
+ MOZ_ASSERT(false, "Deserialize failed!");
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIURI> uri = do_QueryInterface(serializable);
+ MOZ_ASSERT(uri);
+
+ return uri.forget();
+}
+
+already_AddRefed<nsIURI>
+DeserializeURI(const OptionalURIParams& aParams)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIURI> uri;
+
+ switch (aParams.type()) {
+ case OptionalURIParams::Tvoid_t:
+ break;
+
+ case OptionalURIParams::TURIParams:
+ uri = DeserializeURI(aParams.get_URIParams());
+ break;
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ return uri.forget();
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/glue/URIUtils.h b/ipc/glue/URIUtils.h
new file mode 100644
index 000000000..74f82a13b
--- /dev/null
+++ b/ipc/glue/URIUtils.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_ipc_URIUtils_h
+#define mozilla_ipc_URIUtils_h
+
+#include "mozilla/ipc/URIParams.h"
+#include "nsCOMPtr.h"
+#include "nsIURI.h"
+
+namespace mozilla {
+namespace ipc {
+
+void
+SerializeURI(nsIURI* aURI,
+ URIParams& aParams);
+
+void
+SerializeURI(nsIURI* aURI,
+ OptionalURIParams& aParams);
+
+already_AddRefed<nsIURI>
+DeserializeURI(const URIParams& aParams);
+
+already_AddRefed<nsIURI>
+DeserializeURI(const OptionalURIParams& aParams);
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_URIUtils_h
diff --git a/ipc/glue/WindowsMessageLoop.cpp b/ipc/glue/WindowsMessageLoop.cpp
new file mode 100644
index 000000000..8057ee25d
--- /dev/null
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -0,0 +1,1553 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 "WindowsMessageLoop.h"
+#include "Neutering.h"
+#include "MessageChannel.h"
+
+#include "nsAutoPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsIXULAppInfo.h"
+#include "WinUtils.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/PaintTracker.h"
+#include "mozilla/WindowsVersion.h"
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace mozilla::ipc::windows;
+
+/**
+ * The Windows-only code below exists to solve a general problem with deadlocks
+ * that we experience when sending synchronous IPC messages to processes that
+ * contain native windows (i.e. HWNDs). Windows (the OS) sends synchronous
+ * messages between parent and child HWNDs in multiple circumstances (e.g.
+ * WM_PARENTNOTIFY, WM_NCACTIVATE, etc.), even when those HWNDs are controlled
+ * by different threads or different processes. Thus we can very easily end up
+ * in a deadlock by a call stack like the following:
+ *
+ * Process A:
+ * - CreateWindow(...) creates a "parent" HWND.
+ * - SendCreateChildWidget(HWND) is a sync IPC message that sends the "parent"
+ * HWND over to Process B. Process A blocks until a response is received
+ * from Process B.
+ *
+ * Process B:
+ * - RecvCreateWidget(HWND) gets the "parent" HWND from Process A.
+ * - CreateWindow(..., HWND) creates a "child" HWND with the parent from
+ * process A.
+ * - Windows (the OS) generates a WM_PARENTNOTIFY message that is sent
+ * synchronously to Process A. Process B blocks until a response is
+ * received from Process A. Process A, however, is blocked and cannot
+ * process the message. Both processes are deadlocked.
+ *
+ * The example above has a few different workarounds (e.g. setting the
+ * WS_EX_NOPARENTNOTIFY style on the child window) but the general problem is
+ * persists. Once two HWNDs are parented we must not block their owning
+ * threads when manipulating either HWND.
+ *
+ * Windows requires any application that hosts native HWNDs to always process
+ * messages or risk deadlock. Given our architecture the only way to meet
+ * Windows' requirement and allow for synchronous IPC messages is to pump a
+ * miniature message loop during a sync IPC call. We avoid processing any
+ * queued messages during the loop (with one exception, see below), but
+ * "nonqueued" messages (see
+ * http://msdn.microsoft.com/en-us/library/ms644927(VS.85).aspx under the
+ * section "Nonqueued messages") cannot be avoided. Those messages are trapped
+ * in a special window procedure where we can either ignore the message or
+ * process it in some fashion.
+ *
+ * Queued and "non-queued" messages will be processed during Interrupt calls if
+ * modal UI related api calls block an Interrupt in-call in the child. To prevent
+ * windows from freezing, and to allow concurrent processing of critical
+ * events (such as painting), we spin a native event dispatch loop while
+ * these in-calls are blocked.
+ */
+
+#if defined(ACCESSIBILITY)
+// pulled from accessibility's win utils
+extern const wchar_t* kPropNameTabContent;
+#endif
+
+// widget related message id constants we need to defer
+namespace mozilla {
+namespace widget {
+extern UINT sAppShellGeckoMsgId;
+}
+}
+
+namespace {
+
+const wchar_t kOldWndProcProp[] = L"MozillaIPCOldWndProc";
+const wchar_t k3rdPartyWindowProp[] = L"Mozilla3rdPartyWindow";
+
+// This isn't defined before Windows XP.
+enum { WM_XP_THEMECHANGED = 0x031A };
+
+char16_t gAppMessageWindowName[256] = { 0 };
+int32_t gAppMessageWindowNameLength = 0;
+
+nsTArray<HWND>* gNeuteredWindows = nullptr;
+
+typedef nsTArray<nsAutoPtr<DeferredMessage> > DeferredMessageArray;
+DeferredMessageArray* gDeferredMessages = nullptr;
+
+HHOOK gDeferredGetMsgHook = nullptr;
+HHOOK gDeferredCallWndProcHook = nullptr;
+
+DWORD gUIThreadId = 0;
+HWND gCOMWindow = 0;
+// Once initialized, gWinEventHook is never unhooked. We save the handle so
+// that we can check whether or not the hook is initialized.
+HWINEVENTHOOK gWinEventHook = nullptr;
+const wchar_t kCOMWindowClassName[] = L"OleMainThreadWndClass";
+
+// WM_GETOBJECT id pulled from uia headers
+#define MOZOBJID_UIAROOT -25
+
+HWND
+FindCOMWindow()
+{
+ MOZ_ASSERT(gUIThreadId);
+
+ HWND last = 0;
+ while ((last = FindWindowExW(HWND_MESSAGE, last, kCOMWindowClassName, NULL))) {
+ if (GetWindowThreadProcessId(last, NULL) == gUIThreadId) {
+ return last;
+ }
+ }
+
+ return (HWND)0;
+}
+
+void CALLBACK
+WinEventHook(HWINEVENTHOOK aWinEventHook, DWORD aEvent, HWND aHwnd,
+ LONG aIdObject, LONG aIdChild, DWORD aEventThread,
+ DWORD aMsEventTime)
+{
+ MOZ_ASSERT(aWinEventHook == gWinEventHook);
+ MOZ_ASSERT(gUIThreadId == aEventThread);
+ switch (aEvent) {
+ case EVENT_OBJECT_CREATE: {
+ if (aIdObject != OBJID_WINDOW || aIdChild != CHILDID_SELF) {
+ // Not an event we're interested in
+ return;
+ }
+ wchar_t classBuf[256] = {0};
+ int result = ::GetClassNameW(aHwnd, classBuf,
+ MOZ_ARRAY_LENGTH(classBuf));
+ if (result != (MOZ_ARRAY_LENGTH(kCOMWindowClassName) - 1) ||
+ wcsncmp(kCOMWindowClassName, classBuf, result)) {
+ // Not a class we're interested in
+ return;
+ }
+ MOZ_ASSERT(FindCOMWindow() == aHwnd);
+ gCOMWindow = aHwnd;
+ break;
+ }
+ case EVENT_OBJECT_DESTROY: {
+ if (aHwnd == gCOMWindow && aIdObject == OBJID_WINDOW) {
+ MOZ_ASSERT(aIdChild == CHILDID_SELF);
+ gCOMWindow = 0;
+ }
+ break;
+ }
+ default: {
+ return;
+ }
+ }
+}
+
+LRESULT CALLBACK
+DeferredMessageHook(int nCode,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ // XXX This function is called for *both* the WH_CALLWNDPROC hook and the
+ // WH_GETMESSAGE hook, but they have different parameters. We don't
+ // use any of them except nCode which has the same meaning.
+
+ // Only run deferred messages if all of these conditions are met:
+ // 1. The |nCode| indicates that this hook should do something.
+ // 2. We have deferred messages to run.
+ // 3. We're not being called from the PeekMessage within the WaitFor*Notify
+ // function (indicated with MessageChannel::IsPumpingMessages). We really
+ // only want to run after returning to the main event loop.
+ if (nCode >= 0 && gDeferredMessages && !MessageChannel::IsPumpingMessages()) {
+ NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
+ "These hooks must be set if we're being called!");
+ NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
+
+ // Unset hooks first, in case we reenter below.
+ UnhookWindowsHookEx(gDeferredGetMsgHook);
+ UnhookWindowsHookEx(gDeferredCallWndProcHook);
+ gDeferredGetMsgHook = 0;
+ gDeferredCallWndProcHook = 0;
+
+ // Unset the global and make sure we delete it when we're done here.
+ nsAutoPtr<DeferredMessageArray> messages(gDeferredMessages);
+ gDeferredMessages = nullptr;
+
+ // Run all the deferred messages in order.
+ uint32_t count = messages->Length();
+ for (uint32_t index = 0; index < count; index++) {
+ messages->ElementAt(index)->Run();
+ }
+ }
+
+ // Always call the next hook.
+ return CallNextHookEx(nullptr, nCode, wParam, lParam);
+}
+
+void
+ScheduleDeferredMessageRun()
+{
+ if (gDeferredMessages &&
+ !(gDeferredGetMsgHook && gDeferredCallWndProcHook)) {
+ NS_ASSERTION(gDeferredMessages->Length(), "No deferred messages?!");
+
+ gDeferredGetMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, DeferredMessageHook,
+ nullptr, gUIThreadId);
+ gDeferredCallWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC,
+ DeferredMessageHook, nullptr,
+ gUIThreadId);
+ NS_ASSERTION(gDeferredGetMsgHook && gDeferredCallWndProcHook,
+ "Failed to set hooks!");
+ }
+}
+
+static void
+DumpNeuteredMessage(HWND hwnd, UINT uMsg)
+{
+#ifdef DEBUG
+ nsAutoCString log("Received \"nonqueued\" ");
+ // classify messages
+ if (uMsg < WM_USER) {
+ int idx = 0;
+ while (mozilla::widget::gAllEvents[idx].mId != (long)uMsg &&
+ mozilla::widget::gAllEvents[idx].mStr != nullptr) {
+ idx++;
+ }
+ if (mozilla::widget::gAllEvents[idx].mStr) {
+ log.AppendPrintf("ui message \"%s\"", mozilla::widget::gAllEvents[idx].mStr);
+ } else {
+ log.AppendPrintf("ui message (0x%X)", uMsg);
+ }
+ } else if (uMsg >= WM_USER && uMsg < WM_APP) {
+ log.AppendPrintf("WM_USER message (0x%X)", uMsg);
+ } else if (uMsg >= WM_APP && uMsg < 0xC000) {
+ log.AppendPrintf("WM_APP message (0x%X)", uMsg);
+ } else if (uMsg >= 0xC000 && uMsg < 0x10000) {
+ log.AppendPrintf("registered windows message (0x%X)", uMsg);
+ } else {
+ log.AppendPrintf("system message (0x%X)", uMsg);
+ }
+
+ log.AppendLiteral(" during a synchronous IPC message for window ");
+ log.AppendPrintf("0x%X", hwnd);
+
+ wchar_t className[256] = { 0 };
+ if (GetClassNameW(hwnd, className, sizeof(className) - 1) > 0) {
+ log.AppendLiteral(" (\"");
+ log.Append(NS_ConvertUTF16toUTF8((char16_t*)className));
+ log.AppendLiteral("\")");
+ }
+
+ log.AppendLiteral(", sending it to DefWindowProc instead of the normal "
+ "window procedure.");
+ NS_ERROR(log.get());
+#endif
+}
+
+LRESULT
+ProcessOrDeferMessage(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ DeferredMessage* deferred = nullptr;
+
+ // Most messages ask for 0 to be returned if the message is processed.
+ LRESULT res = 0;
+
+ switch (uMsg) {
+ // Messages that can be deferred as-is. These must not contain pointers in
+ // their wParam or lParam arguments!
+ case WM_ACTIVATE:
+ case WM_ACTIVATEAPP:
+ case WM_CANCELMODE:
+ case WM_CAPTURECHANGED:
+ case WM_CHILDACTIVATE:
+ case WM_DESTROY:
+ case WM_ENABLE:
+ case WM_IME_NOTIFY:
+ case WM_IME_SETCONTEXT:
+ case WM_KILLFOCUS:
+ case WM_MOUSEWHEEL:
+ case WM_NCDESTROY:
+ case WM_PARENTNOTIFY:
+ case WM_SETFOCUS:
+ case WM_SYSCOMMAND:
+ case WM_DISPLAYCHANGE:
+ case WM_SHOWWINDOW: // Intentional fall-through.
+ case WM_XP_THEMECHANGED: {
+ deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ case WM_DEVICECHANGE:
+ case WM_POWERBROADCAST:
+ case WM_NCACTIVATE: // Intentional fall-through.
+ case WM_SETCURSOR: {
+ // Friggin unconventional return value...
+ res = TRUE;
+ deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ case WM_MOUSEACTIVATE: {
+ res = MA_NOACTIVATE;
+ deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ // These messages need to use the RedrawWindow function to generate the
+ // right kind of message. We can't simply fake them as the MSDN docs say
+ // explicitly that paint messages should not be sent by an application.
+ case WM_ERASEBKGND: {
+ UINT flags = RDW_INVALIDATE | RDW_ERASE | RDW_NOINTERNALPAINT |
+ RDW_NOFRAME | RDW_NOCHILDREN | RDW_ERASENOW;
+ deferred = new DeferredRedrawMessage(hwnd, flags);
+ break;
+ }
+
+ // This message will generate a WM_PAINT message if there are invalid
+ // areas.
+ case WM_PAINT: {
+ deferred = new DeferredUpdateMessage(hwnd);
+ break;
+ }
+
+ // This message holds a string in its lParam that we must copy.
+ case WM_SETTINGCHANGE: {
+ deferred = new DeferredSettingChangeMessage(hwnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ // These messages are faked via a call to SetWindowPos.
+ case WM_WINDOWPOSCHANGED: {
+ deferred = new DeferredWindowPosMessage(hwnd, lParam);
+ break;
+ }
+ case WM_NCCALCSIZE: {
+ deferred = new DeferredWindowPosMessage(hwnd, lParam, true, wParam);
+ break;
+ }
+
+ case WM_COPYDATA: {
+ deferred = new DeferredCopyDataMessage(hwnd, uMsg, wParam, lParam);
+ res = TRUE;
+ break;
+ }
+
+ case WM_STYLECHANGED: {
+ deferred = new DeferredStyleChangeMessage(hwnd, wParam, lParam);
+ break;
+ }
+
+ case WM_SETICON: {
+ deferred = new DeferredSetIconMessage(hwnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ // Messages that are safe to pass to DefWindowProc go here.
+ case WM_ENTERIDLE:
+ case WM_GETICON:
+ case WM_NCPAINT: // (never trap nc paint events)
+ case WM_GETMINMAXINFO:
+ case WM_GETTEXT:
+ case WM_NCHITTEST:
+ case WM_STYLECHANGING: // Intentional fall-through.
+ case WM_WINDOWPOSCHANGING:
+ case WM_GETTEXTLENGTH: {
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+
+ // Just return, prevents DefWindowProc from messaging the window
+ // syncronously with other events, which may be deferred. Prevents
+ // random shutdown of aero composition on the window.
+ case WM_SYNCPAINT:
+ return 0;
+
+ // This message causes QuickTime to make re-entrant calls.
+ // Simply discarding it doesn't seem to hurt anything.
+ case WM_APP-1:
+ return 0;
+
+ // We only support a query for our IAccessible or UIA pointers.
+ // This should be safe, and needs to be sync.
+#if defined(ACCESSIBILITY)
+ case WM_GETOBJECT: {
+ if (!::GetPropW(hwnd, k3rdPartyWindowProp)) {
+ DWORD objId = static_cast<DWORD>(lParam);
+ if ((objId == OBJID_CLIENT || objId == MOZOBJID_UIAROOT)) {
+ WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
+ if (oldWndProc) {
+ return CallWindowProcW(oldWndProc, hwnd, uMsg, wParam, lParam);
+ }
+ }
+ }
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+#endif // ACCESSIBILITY
+
+ default: {
+ // Unknown messages only are logged in debug builds and sent to
+ // DefWindowProc.
+ if (uMsg && uMsg == mozilla::widget::sAppShellGeckoMsgId) {
+ // Widget's registered native event callback
+ deferred = new DeferredSendMessage(hwnd, uMsg, wParam, lParam);
+ }
+ }
+ }
+
+ // No deferred message was created and we land here, this is an
+ // unhandled message.
+ if (!deferred) {
+ DumpNeuteredMessage(hwnd, uMsg);
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+
+ // Create the deferred message array if it doesn't exist already.
+ if (!gDeferredMessages) {
+ gDeferredMessages = new nsTArray<nsAutoPtr<DeferredMessage> >(20);
+ NS_ASSERTION(gDeferredMessages, "Out of memory!");
+ }
+
+ // Save for later. The array takes ownership of |deferred|.
+ gDeferredMessages->AppendElement(deferred);
+ return res;
+}
+
+} // namespace
+
+// We need the pointer value of this in PluginInstanceChild.
+LRESULT CALLBACK
+NeuteredWindowProc(HWND hwnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ WNDPROC oldWndProc = (WNDPROC)GetProp(hwnd, kOldWndProcProp);
+ if (!oldWndProc) {
+ // We should really never ever get here.
+ NS_ERROR("No old wndproc!");
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+ }
+
+ // See if we care about this message. We may either ignore it, send it to
+ // DefWindowProc, or defer it for later.
+ return ProcessOrDeferMessage(hwnd, uMsg, wParam, lParam);
+}
+
+namespace {
+
+static bool
+WindowIsDeferredWindow(HWND hWnd)
+{
+ if (!IsWindow(hWnd)) {
+ NS_WARNING("Window has died!");
+ return false;
+ }
+
+ char16_t buffer[256] = { 0 };
+ int length = GetClassNameW(hWnd, (wchar_t*)buffer, sizeof(buffer) - 1);
+ if (length <= 0) {
+ NS_WARNING("Failed to get class name!");
+ return false;
+ }
+
+#if defined(ACCESSIBILITY)
+ // Tab content creates a window that responds to accessible WM_GETOBJECT
+ // calls. This window can safely be ignored.
+ if (::GetPropW(hWnd, kPropNameTabContent)) {
+ return false;
+ }
+#endif
+
+ // Common mozilla windows we must defer messages to.
+ nsDependentString className(buffer, length);
+ if (StringBeginsWith(className, NS_LITERAL_STRING("Mozilla")) ||
+ StringBeginsWith(className, NS_LITERAL_STRING("Gecko")) ||
+ className.EqualsLiteral("nsToolkitClass") ||
+ className.EqualsLiteral("nsAppShell:EventWindowClass")) {
+ return true;
+ }
+
+ // Plugin windows that can trigger ipc calls in child:
+ // 'ShockwaveFlashFullScreen' - flash fullscreen window
+ // 'QTNSHIDDEN' - QuickTime
+ // 'AGFullScreenWinClass' - silverlight fullscreen window
+ if (className.EqualsLiteral("ShockwaveFlashFullScreen") ||
+ className.EqualsLiteral("QTNSHIDDEN") ||
+ className.EqualsLiteral("AGFullScreenWinClass")) {
+ SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
+ return true;
+ }
+
+ // Google Earth bridging msg window between the plugin instance and a separate
+ // earth process. The earth process can trigger a plugin incall on the browser
+ // at any time, which is badness if the instance is already making an incall.
+ if (className.EqualsLiteral("__geplugin_bridge_window__")) {
+ SetPropW(hWnd, k3rdPartyWindowProp, (HANDLE)1);
+ return true;
+ }
+
+ // nsNativeAppSupport makes a window like "FirefoxMessageWindow" based on the
+ // toolkit app's name. It's pretty expensive to calculate this so we only try
+ // once.
+ if (gAppMessageWindowNameLength == 0) {
+ nsCOMPtr<nsIXULAppInfo> appInfo =
+ do_GetService("@mozilla.org/xre/app-info;1");
+ if (appInfo) {
+ nsAutoCString appName;
+ if (NS_SUCCEEDED(appInfo->GetName(appName))) {
+ appName.AppendLiteral("MessageWindow");
+ nsDependentString windowName(gAppMessageWindowName);
+ CopyUTF8toUTF16(appName, windowName);
+ gAppMessageWindowNameLength = windowName.Length();
+ }
+ }
+
+ // Don't try again if that failed.
+ if (gAppMessageWindowNameLength == 0) {
+ gAppMessageWindowNameLength = -1;
+ }
+ }
+
+ if (gAppMessageWindowNameLength != -1 &&
+ className.Equals(nsDependentString(gAppMessageWindowName,
+ gAppMessageWindowNameLength))) {
+ return true;
+ }
+
+ return false;
+}
+
+bool
+NeuterWindowProcedure(HWND hWnd)
+{
+ if (!WindowIsDeferredWindow(hWnd)) {
+ // Some other kind of window, skip.
+ return false;
+ }
+
+ NS_ASSERTION(!GetProp(hWnd, kOldWndProcProp), "This should always be null!");
+
+ // It's possible to get nullptr out of SetWindowLongPtr, and the only way to
+ // know if that's a valid old value is to use GetLastError. Clear the error
+ // here so we can tell.
+ SetLastError(ERROR_SUCCESS);
+
+ LONG_PTR currentWndProc =
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)NeuteredWindowProc);
+ if (!currentWndProc) {
+ if (ERROR_SUCCESS == GetLastError()) {
+ // No error, so we set something and must therefore reset it.
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
+ }
+ return false;
+ }
+
+ NS_ASSERTION(currentWndProc != (LONG_PTR)NeuteredWindowProc,
+ "This shouldn't be possible!");
+
+ if (!SetProp(hWnd, kOldWndProcProp, (HANDLE)currentWndProc)) {
+ // Cleanup
+ NS_WARNING("SetProp failed!");
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, currentWndProc);
+ RemovePropW(hWnd, kOldWndProcProp);
+ RemovePropW(hWnd, k3rdPartyWindowProp);
+ return false;
+ }
+
+ return true;
+}
+
+void
+RestoreWindowProcedure(HWND hWnd)
+{
+ NS_ASSERTION(WindowIsDeferredWindow(hWnd),
+ "Not a deferred window, this shouldn't be in our list!");
+ LONG_PTR oldWndProc = (LONG_PTR)GetProp(hWnd, kOldWndProcProp);
+ if (oldWndProc) {
+ NS_ASSERTION(oldWndProc != (LONG_PTR)NeuteredWindowProc,
+ "This shouldn't be possible!");
+
+ DebugOnly<LONG_PTR> currentWndProc =
+ SetWindowLongPtr(hWnd, GWLP_WNDPROC, oldWndProc);
+ NS_ASSERTION(currentWndProc == (LONG_PTR)NeuteredWindowProc,
+ "This should never be switched out from under us!");
+ }
+ RemovePropW(hWnd, kOldWndProcProp);
+ RemovePropW(hWnd, k3rdPartyWindowProp);
+}
+
+LRESULT CALLBACK
+CallWindowProcedureHook(int nCode,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ if (nCode >= 0) {
+ NS_ASSERTION(gNeuteredWindows, "This should never be null!");
+
+ HWND hWnd = reinterpret_cast<CWPSTRUCT*>(lParam)->hwnd;
+
+ if (!gNeuteredWindows->Contains(hWnd) &&
+ !SuppressedNeuteringRegion::IsNeuteringSuppressed() &&
+ NeuterWindowProcedure(hWnd)) {
+ if (!gNeuteredWindows->AppendElement(hWnd)) {
+ NS_ERROR("Out of memory!");
+ RestoreWindowProcedure(hWnd);
+ }
+ }
+ }
+ return CallNextHookEx(nullptr, nCode, wParam, lParam);
+}
+
+inline void
+AssertWindowIsNotNeutered(HWND hWnd)
+{
+#ifdef DEBUG
+ // Make sure our neutered window hook isn't still in place.
+ LONG_PTR wndproc = GetWindowLongPtr(hWnd, GWLP_WNDPROC);
+ NS_ASSERTION(wndproc != (LONG_PTR)NeuteredWindowProc, "Window is neutered!");
+#endif
+}
+
+void
+UnhookNeuteredWindows()
+{
+ if (!gNeuteredWindows)
+ return;
+ uint32_t count = gNeuteredWindows->Length();
+ for (uint32_t index = 0; index < count; index++) {
+ RestoreWindowProcedure(gNeuteredWindows->ElementAt(index));
+ }
+ gNeuteredWindows->Clear();
+}
+
+// This timeout stuff assumes a sane value of mTimeoutMs (less than the overflow
+// value for GetTickCount(), which is something like 50 days). It uses the
+// cheapest (and least accurate) method supported by Windows 2000.
+
+struct TimeoutData
+{
+ DWORD startTicks;
+ DWORD targetTicks;
+};
+
+void
+InitTimeoutData(TimeoutData* aData,
+ int32_t aTimeoutMs)
+{
+ aData->startTicks = GetTickCount();
+ if (!aData->startTicks) {
+ // How unlikely is this!
+ aData->startTicks++;
+ }
+ aData->targetTicks = aData->startTicks + aTimeoutMs;
+}
+
+
+bool
+TimeoutHasExpired(const TimeoutData& aData)
+{
+ if (!aData.startTicks) {
+ return false;
+ }
+
+ DWORD now = GetTickCount();
+
+ if (aData.targetTicks < aData.startTicks) {
+ // Overflow
+ return now < aData.startTicks && now >= aData.targetTicks;
+ }
+ return now >= aData.targetTicks;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+namespace windows {
+
+void
+InitUIThread()
+{
+ // If we aren't setup before a call to NotifyWorkerThread, we'll hang
+ // on startup.
+ if (!gUIThreadId) {
+ gUIThreadId = GetCurrentThreadId();
+ }
+
+ MOZ_ASSERT(gUIThreadId);
+ MOZ_ASSERT(gUIThreadId == GetCurrentThreadId(),
+ "Called InitUIThread multiple times on different threads!");
+
+ if (!gWinEventHook) {
+ gWinEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY,
+ NULL, &WinEventHook, GetCurrentProcessId(),
+ gUIThreadId, WINEVENT_OUTOFCONTEXT);
+
+ // We need to execute this after setting the hook in case the OLE window
+ // already existed.
+ gCOMWindow = FindCOMWindow();
+ }
+ MOZ_ASSERT(gWinEventHook);
+}
+
+} // namespace windows
+} // namespace ipc
+} // namespace mozilla
+
+// See SpinInternalEventLoop below
+MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool interrupt)
+ : mInterrupt(interrupt)
+ , mSpinNestedEvents(false)
+ , mListenerNotified(false)
+ , mChannel(channel)
+ , mPrev(mChannel->mTopFrame)
+ , mStaticPrev(sStaticTopFrame)
+{
+ // Only track stack frames when Windows message deferral behavior
+ // is request for the channel.
+ if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
+ return;
+ }
+
+ mChannel->mTopFrame = this;
+ sStaticTopFrame = this;
+
+ if (!mStaticPrev) {
+ NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
+ gNeuteredWindows = new AutoTArray<HWND, 20>();
+ NS_ASSERTION(gNeuteredWindows, "Out of memory!");
+ }
+}
+
+MessageChannel::SyncStackFrame::~SyncStackFrame()
+{
+ if (!(mChannel->GetChannelFlags() & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
+ return;
+ }
+
+ NS_ASSERTION(this == mChannel->mTopFrame,
+ "Mismatched interrupt stack frames");
+ NS_ASSERTION(this == sStaticTopFrame,
+ "Mismatched static Interrupt stack frames");
+
+ mChannel->mTopFrame = mPrev;
+ sStaticTopFrame = mStaticPrev;
+
+ if (!mStaticPrev) {
+ NS_ASSERTION(gNeuteredWindows, "Bad pointer!");
+ delete gNeuteredWindows;
+ gNeuteredWindows = nullptr;
+ }
+}
+
+MessageChannel::SyncStackFrame* MessageChannel::sStaticTopFrame;
+
+// nsAppShell's notification that gecko events are being processed.
+// If we are here and there is an Interrupt Incall active, we are spinning
+// a nested gecko event loop. In which case the remote process needs
+// to know about it.
+void /* static */
+MessageChannel::NotifyGeckoEventDispatch()
+{
+ // sStaticTopFrame is only valid for Interrupt channels
+ if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified)
+ return;
+
+ sStaticTopFrame->mListenerNotified = true;
+ MessageChannel* channel = static_cast<MessageChannel*>(sStaticTopFrame->mChannel);
+ channel->Listener()->ProcessRemoteNativeEventsInInterruptCall();
+}
+
+// invoked by the module that receives the spin event loop
+// message.
+void
+MessageChannel::ProcessNativeEventsInInterruptCall()
+{
+ NS_ASSERTION(GetCurrentThreadId() == gUIThreadId,
+ "Shouldn't be on a non-main thread in here!");
+ if (!mTopFrame) {
+ NS_ERROR("Spin logic error: no Interrupt frame");
+ return;
+ }
+
+ mTopFrame->mSpinNestedEvents = true;
+}
+
+// Spin loop is called in place of WaitFor*Notify when modal ui is being shown
+// in a child. There are some intricacies in using it however. Spin loop is
+// enabled for a particular Interrupt frame by the client calling
+// MessageChannel::ProcessNativeEventsInInterrupt().
+// This call can be nested for multiple Interrupt frames in a single plugin or
+// multiple unrelated plugins.
+void
+MessageChannel::SpinInternalEventLoop()
+{
+ if (mozilla::PaintTracker::IsPainting()) {
+ NS_RUNTIMEABORT("Don't spin an event loop while painting.");
+ }
+
+ NS_ASSERTION(mTopFrame && mTopFrame->mSpinNestedEvents,
+ "Spinning incorrectly");
+
+ // Nested windows event loop we trigger when the child enters into modal
+ // event loops.
+
+ // Note, when we return, we always reset the notify worker event. So there's
+ // no need to reset it on return here.
+
+ do {
+ MSG msg = { 0 };
+
+ // Don't get wrapped up in here if the child connection dies.
+ {
+ MonitorAutoLock lock(*mMonitor);
+ if (!Connected()) {
+ return;
+ }
+ }
+
+ // Retrieve window or thread messages
+ if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ // The child UI should have been destroyed before the app is closed, in
+ // which case, we should never get this here.
+ if (msg.message == WM_QUIT) {
+ NS_ERROR("WM_QUIT received in SpinInternalEventLoop!");
+ } else {
+ TranslateMessage(&msg);
+ ::DispatchMessageW(&msg);
+ return;
+ }
+ }
+
+ // Note, give dispatching windows events priority over checking if
+ // mEvent is signaled, otherwise heavy ipc traffic can cause jittery
+ // playback of video. We'll exit out on each disaptch above, so ipc
+ // won't get starved.
+
+ // Wait for UI events or a signal from the io thread.
+ DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
+ QS_ALLINPUT);
+ if (result == WAIT_OBJECT_0) {
+ // Our NotifyWorkerThread event was signaled
+ return;
+ }
+ } while (true);
+}
+
+static inline bool
+IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
+{
+ return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
+ (aTimeout <= (PR_IntervalNow() - aStart));
+}
+
+static HHOOK gWindowHook;
+
+static inline void
+StartNeutering()
+{
+ MOZ_ASSERT(gUIThreadId);
+ MOZ_ASSERT(!gWindowHook);
+ NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
+ "Shouldn't be pumping already!");
+ MessageChannel::SetIsPumpingMessages(true);
+ gWindowHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
+ nullptr, gUIThreadId);
+ NS_ASSERTION(gWindowHook, "Failed to set hook!");
+}
+
+static void
+StopNeutering()
+{
+ MOZ_ASSERT(MessageChannel::IsPumpingMessages());
+ ::UnhookWindowsHookEx(gWindowHook);
+ gWindowHook = NULL;
+ ::UnhookNeuteredWindows();
+ // Before returning we need to set a hook to run any deferred messages that
+ // we received during the IPC call. The hook will unset itself as soon as
+ // someone else calls GetMessage, PeekMessage, or runs code that generates
+ // a "nonqueued" message.
+ ::ScheduleDeferredMessageRun();
+ MessageChannel::SetIsPumpingMessages(false);
+}
+
+NeuteredWindowRegion::NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+ : mNeuteredByThis(!gWindowHook && aDoNeuter)
+{
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ if (mNeuteredByThis) {
+ StartNeutering();
+ }
+}
+
+NeuteredWindowRegion::~NeuteredWindowRegion()
+{
+ if (gWindowHook && mNeuteredByThis) {
+ StopNeutering();
+ }
+}
+
+void
+NeuteredWindowRegion::PumpOnce()
+{
+ if (!gWindowHook) {
+ // This should be a no-op if nothing has been neutered.
+ return;
+ }
+
+ MSG msg = {0};
+ // Pump any COM messages so that we don't hang due to STA marshaling.
+ if (gCOMWindow && ::PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessageW(&msg);
+ }
+ // Expunge any nonqueued messages on the current thread.
+ ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
+}
+
+DeneuteredWindowRegion::DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
+ : mReneuter(gWindowHook != NULL)
+{
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ if (mReneuter) {
+ StopNeutering();
+ }
+}
+
+DeneuteredWindowRegion::~DeneuteredWindowRegion()
+{
+ if (mReneuter) {
+ StartNeutering();
+ }
+}
+
+SuppressedNeuteringRegion::SuppressedNeuteringRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
+ : mReenable(::gUIThreadId == ::GetCurrentThreadId() && ::gWindowHook)
+{
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ if (mReenable) {
+ MOZ_ASSERT(!sSuppressNeutering);
+ sSuppressNeutering = true;
+ }
+}
+
+SuppressedNeuteringRegion::~SuppressedNeuteringRegion()
+{
+ if (mReenable) {
+ MOZ_ASSERT(sSuppressNeutering);
+ sSuppressNeutering = false;
+ }
+}
+
+bool SuppressedNeuteringRegion::sSuppressNeutering = false;
+
+#if defined(ACCESSIBILITY)
+bool
+MessageChannel::WaitForSyncNotifyWithA11yReentry()
+{
+ mMonitor->AssertCurrentThreadOwns();
+ MonitorAutoUnlock unlock(*mMonitor);
+
+ const DWORD waitStart = ::GetTickCount();
+ DWORD elapsed = 0;
+ DWORD timeout = mTimeoutMs == kNoTimeout ? INFINITE :
+ static_cast<DWORD>(mTimeoutMs);
+ bool timedOut = false;
+
+ while (true) {
+ { // Scope for lock
+ MonitorAutoLock lock(*mMonitor);
+ if (!Connected()) {
+ break;
+ }
+ }
+ if (timeout != static_cast<DWORD>(kNoTimeout)) {
+ elapsed = ::GetTickCount() - waitStart;
+ }
+ if (elapsed >= timeout) {
+ timedOut = true;
+ break;
+ }
+ DWORD waitResult = 0;
+ ::SetLastError(ERROR_SUCCESS);
+ HRESULT hr = ::CoWaitForMultipleHandles(COWAIT_ALERTABLE,
+ timeout - elapsed,
+ 1, &mEvent, &waitResult);
+ if (hr == RPC_S_CALLPENDING) {
+ timedOut = true;
+ break;
+ }
+ if (hr == S_OK) {
+ if (waitResult == 0) {
+ // mEvent is signaled
+ BOOL success = ::ResetEvent(mEvent);
+ if (!success) {
+ gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
+ "WindowsMessageChannel::WaitForSyncNotifyWithA11yReentry failed to reset event. GetLastError: " <<
+ GetLastError();
+ }
+ break;
+ }
+ if (waitResult == WAIT_IO_COMPLETION) {
+ // APC fired, keep waiting
+ continue;
+ }
+ }
+ NS_ERROR("CoWaitForMultipleHandles failed");
+ break;
+ }
+
+ return WaitResponse(timedOut);
+}
+#endif
+
+bool
+MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages)
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
+
+#if defined(ACCESSIBILITY)
+ if (IsVistaOrLater() && (mFlags & REQUIRE_A11Y_REENTRY)) {
+ MOZ_ASSERT(!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION));
+ return WaitForSyncNotifyWithA11yReentry();
+ }
+#endif
+
+ // Use a blocking wait if this channel does not require
+ // Windows message deferral behavior.
+ if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION) || !aHandleWindowsMessages) {
+ PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ?
+ PR_INTERVAL_NO_TIMEOUT :
+ PR_MillisecondsToInterval(mTimeoutMs);
+ PRIntervalTime waitStart = 0;
+
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ waitStart = PR_IntervalNow();
+ }
+
+ MOZ_ASSERT(!mIsSyncWaitingOnNonMainThread);
+ mIsSyncWaitingOnNonMainThread = true;
+
+ mMonitor->Wait(timeout);
+
+ MOZ_ASSERT(mIsSyncWaitingOnNonMainThread);
+ mIsSyncWaitingOnNonMainThread = false;
+
+ // If the timeout didn't expire, we know we received an event. The
+ // converse is not true.
+ return WaitResponse(timeout == PR_INTERVAL_NO_TIMEOUT ?
+ false : IsTimeoutExpired(waitStart, timeout));
+ }
+
+ NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
+ "Shouldn't be here for channels that don't use message deferral!");
+ NS_ASSERTION(mTopFrame && !mTopFrame->mInterrupt,
+ "Top frame is not a sync frame!");
+
+ MonitorAutoUnlock unlock(*mMonitor);
+
+ bool timedout = false;
+
+ UINT_PTR timerId = 0;
+ TimeoutData timeoutData = { 0 };
+
+ if (mTimeoutMs != kNoTimeout) {
+ InitTimeoutData(&timeoutData, mTimeoutMs);
+
+ // We only do this to ensure that we won't get stuck in
+ // MsgWaitForMultipleObjects below.
+ timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
+ NS_ASSERTION(timerId, "SetTimer failed!");
+ }
+
+ NeuteredWindowRegion neuteredRgn(true);
+
+ {
+ while (1) {
+ MSG msg = { 0 };
+ // Don't get wrapped up in here if the child connection dies.
+ {
+ MonitorAutoLock lock(*mMonitor);
+ if (!Connected()) {
+ break;
+ }
+ }
+
+ // Wait until we have a message in the queue. MSDN docs are a bit unclear
+ // but it seems that windows from two different threads (and it should be
+ // noted that a thread in another process counts as a "different thread")
+ // will implicitly have their message queues attached if they are parented
+ // to one another. This wait call, then, will return for a message
+ // delivered to *either* thread.
+ DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
+ QS_ALLINPUT);
+ if (result == WAIT_OBJECT_0) {
+ // Our NotifyWorkerThread event was signaled
+ BOOL success = ResetEvent(mEvent);
+ if (!success) {
+ gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
+ "WindowsMessageChannel::WaitForSyncNotify failed to reset event. GetLastError: " <<
+ GetLastError();
+ }
+ break;
+ } else
+ if (result != (WAIT_OBJECT_0 + 1)) {
+ NS_ERROR("Wait failed!");
+ break;
+ }
+
+ if (TimeoutHasExpired(timeoutData)) {
+ // A timeout was specified and we've passed it. Break out.
+ timedout = true;
+ break;
+ }
+
+ // The only way to know on which thread the message was delivered is to
+ // use some logic on the return values of GetQueueStatus and PeekMessage.
+ // PeekMessage will return false if there are no "queued" messages, but it
+ // will run all "nonqueued" messages before returning. So if PeekMessage
+ // returns false and there are no "nonqueued" messages that were run then
+ // we know that the message we woke for was intended for a window on
+ // another thread.
+ bool haveSentMessagesPending =
+ (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
+
+ // Either of the PeekMessage calls below will actually process all
+ // "nonqueued" messages that are pending before returning. If we have
+ // "nonqueued" messages pending then we should have switched out all the
+ // window procedures above. In that case this PeekMessage call won't
+ // actually cause any mozilla code (or plugin code) to run.
+
+ // We have to manually pump all COM messages *after* looking at the queue
+ // queue status but before yielding our thread below.
+ if (gCOMWindow) {
+ if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ ::DispatchMessageW(&msg);
+ }
+ }
+
+ // If the following PeekMessage call fails to return a message for us (and
+ // returns false) and we didn't run any "nonqueued" messages then we must
+ // have woken up for a message designated for a window in another thread.
+ // If we loop immediately then we could enter a tight loop, so we'll give
+ // up our time slice here to let the child process its message.
+ if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
+ !haveSentMessagesPending) {
+ // Message was for child, we should wait a bit.
+ SwitchToThread();
+ }
+ }
+ }
+
+ if (timerId) {
+ KillTimer(nullptr, timerId);
+ timerId = 0;
+ }
+
+ return WaitResponse(timedout);
+}
+
+bool
+MessageChannel::WaitForInterruptNotify()
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
+
+ // Re-use sync notification wait code if this channel does not require
+ // Windows message deferral behavior.
+ if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) {
+ return WaitForSyncNotify(true);
+ }
+
+ if (!InterruptStackDepth() && !AwaitingIncomingMessage()) {
+ // There is currently no way to recover from this condition.
+ NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
+ }
+
+ NS_ASSERTION(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION,
+ "Shouldn't be here for channels that don't use message deferral!");
+ NS_ASSERTION(mTopFrame && mTopFrame->mInterrupt,
+ "Top frame is not a sync frame!");
+
+ MonitorAutoUnlock unlock(*mMonitor);
+
+ bool timedout = false;
+
+ UINT_PTR timerId = 0;
+ TimeoutData timeoutData = { 0 };
+
+ // gWindowHook is used as a flag variable for the loop below: if it is set
+ // and we start to spin a nested event loop, we need to clear the hook and
+ // process deferred/pending messages.
+ while (1) {
+ NS_ASSERTION((!!gWindowHook) == MessageChannel::IsPumpingMessages(),
+ "gWindowHook out of sync with reality");
+
+ if (mTopFrame->mSpinNestedEvents) {
+ if (gWindowHook && timerId) {
+ KillTimer(nullptr, timerId);
+ timerId = 0;
+ }
+ DeneuteredWindowRegion deneuteredRgn;
+ SpinInternalEventLoop();
+ BOOL success = ResetEvent(mEvent);
+ if (!success) {
+ gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
+ "WindowsMessageChannel::WaitForInterruptNotify::SpinNestedEvents failed to reset event. GetLastError: " <<
+ GetLastError();
+ }
+ return true;
+ }
+
+ if (mTimeoutMs != kNoTimeout && !timerId) {
+ InitTimeoutData(&timeoutData, mTimeoutMs);
+ timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
+ NS_ASSERTION(timerId, "SetTimer failed!");
+ }
+
+ NeuteredWindowRegion neuteredRgn(true);
+
+ MSG msg = { 0 };
+
+ // Don't get wrapped up in here if the child connection dies.
+ {
+ MonitorAutoLock lock(*mMonitor);
+ if (!Connected()) {
+ break;
+ }
+ }
+
+ DWORD result = MsgWaitForMultipleObjects(1, &mEvent, FALSE, INFINITE,
+ QS_ALLINPUT);
+ if (result == WAIT_OBJECT_0) {
+ // Our NotifyWorkerThread event was signaled
+ BOOL success = ResetEvent(mEvent);
+ if (!success) {
+ gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
+ "WindowsMessageChannel::WaitForInterruptNotify::WaitForMultipleObjects failed to reset event. GetLastError: " <<
+ GetLastError();
+ }
+ break;
+ } else
+ if (result != (WAIT_OBJECT_0 + 1)) {
+ NS_ERROR("Wait failed!");
+ break;
+ }
+
+ if (TimeoutHasExpired(timeoutData)) {
+ // A timeout was specified and we've passed it. Break out.
+ timedout = true;
+ break;
+ }
+
+ // See MessageChannel's WaitFor*Notify for details.
+ bool haveSentMessagesPending =
+ (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
+
+ // Run all COM messages *after* looking at the queue status.
+ if (gCOMWindow) {
+ if (PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ ::DispatchMessageW(&msg);
+ }
+ }
+
+ // PeekMessage markes the messages as "old" so that they don't wake up
+ // MsgWaitForMultipleObjects every time.
+ if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
+ !haveSentMessagesPending) {
+ // Message was for child, we should wait a bit.
+ SwitchToThread();
+ }
+ }
+
+ if (timerId) {
+ KillTimer(nullptr, timerId);
+ timerId = 0;
+ }
+
+ return WaitResponse(timedout);
+}
+
+void
+MessageChannel::NotifyWorkerThread()
+{
+ mMonitor->AssertCurrentThreadOwns();
+
+ if (mIsSyncWaitingOnNonMainThread) {
+ mMonitor->Notify();
+ return;
+ }
+
+ MOZ_RELEASE_ASSERT(mEvent, "No signal event to set, this is really bad!");
+ if (!SetEvent(mEvent)) {
+ NS_WARNING("Failed to set NotifyWorkerThread event!");
+ gfxDevCrash(mozilla::gfx::LogReason::MessageChannelInvalidHandle) <<
+ "WindowsMessageChannel failed to SetEvent. GetLastError: " <<
+ GetLastError();
+ }
+}
+
+void
+DeferredSendMessage::Run()
+{
+ AssertWindowIsNotNeutered(hWnd);
+ if (!IsWindow(hWnd)) {
+ NS_ERROR("Invalid window!");
+ return;
+ }
+
+ WNDPROC wndproc =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
+ if (!wndproc) {
+ NS_ERROR("Invalid window procedure!");
+ return;
+ }
+
+ CallWindowProc(wndproc, hWnd, message, wParam, lParam);
+}
+
+void
+DeferredRedrawMessage::Run()
+{
+ AssertWindowIsNotNeutered(hWnd);
+ if (!IsWindow(hWnd)) {
+ NS_ERROR("Invalid window!");
+ return;
+ }
+
+#ifdef DEBUG
+ BOOL ret =
+#endif
+ RedrawWindow(hWnd, nullptr, nullptr, flags);
+ NS_ASSERTION(ret, "RedrawWindow failed!");
+}
+
+DeferredUpdateMessage::DeferredUpdateMessage(HWND aHWnd)
+{
+ mWnd = aHWnd;
+ if (!GetUpdateRect(mWnd, &mUpdateRect, FALSE)) {
+ memset(&mUpdateRect, 0, sizeof(RECT));
+ return;
+ }
+ ValidateRect(mWnd, &mUpdateRect);
+}
+
+void
+DeferredUpdateMessage::Run()
+{
+ AssertWindowIsNotNeutered(mWnd);
+ if (!IsWindow(mWnd)) {
+ NS_ERROR("Invalid window!");
+ return;
+ }
+
+ InvalidateRect(mWnd, &mUpdateRect, FALSE);
+#ifdef DEBUG
+ BOOL ret =
+#endif
+ UpdateWindow(mWnd);
+ NS_ASSERTION(ret, "UpdateWindow failed!");
+}
+
+DeferredSettingChangeMessage::DeferredSettingChangeMessage(HWND aHWnd,
+ UINT aMessage,
+ WPARAM aWParam,
+ LPARAM aLParam)
+: DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
+{
+ NS_ASSERTION(aMessage == WM_SETTINGCHANGE, "Wrong message type!");
+ if (aLParam) {
+ lParamString = _wcsdup(reinterpret_cast<const wchar_t*>(aLParam));
+ lParam = reinterpret_cast<LPARAM>(lParamString);
+ }
+ else {
+ lParamString = nullptr;
+ lParam = 0;
+ }
+}
+
+DeferredSettingChangeMessage::~DeferredSettingChangeMessage()
+{
+ free(lParamString);
+}
+
+DeferredWindowPosMessage::DeferredWindowPosMessage(HWND aHWnd,
+ LPARAM aLParam,
+ bool aForCalcSize,
+ WPARAM aWParam)
+{
+ if (aForCalcSize) {
+ if (aWParam) {
+ NCCALCSIZE_PARAMS* arg = reinterpret_cast<NCCALCSIZE_PARAMS*>(aLParam);
+ memcpy(&windowPos, arg->lppos, sizeof(windowPos));
+
+ NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
+ }
+ else {
+ RECT* arg = reinterpret_cast<RECT*>(aLParam);
+ windowPos.hwnd = aHWnd;
+ windowPos.hwndInsertAfter = nullptr;
+ windowPos.x = arg->left;
+ windowPos.y = arg->top;
+ windowPos.cx = arg->right - arg->left;
+ windowPos.cy = arg->bottom - arg->top;
+
+ NS_ASSERTION(arg->right >= arg->left && arg->bottom >= arg->top,
+ "Negative width or height!");
+ }
+ windowPos.flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOOWNERZORDER |
+ SWP_NOZORDER | SWP_DEFERERASE | SWP_NOSENDCHANGING;
+ }
+ else {
+ // Not for WM_NCCALCSIZE
+ WINDOWPOS* arg = reinterpret_cast<WINDOWPOS*>(aLParam);
+ memcpy(&windowPos, arg, sizeof(windowPos));
+
+ NS_ASSERTION(aHWnd == windowPos.hwnd, "Mismatched hwnds!");
+
+ // Windows sends in some private flags sometimes that we can't simply copy.
+ // Filter here.
+ UINT mask = SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_DRAWFRAME |
+ SWP_FRAMECHANGED | SWP_HIDEWINDOW | SWP_NOACTIVATE |
+ SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW |
+ SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE |
+ SWP_NOZORDER | SWP_SHOWWINDOW;
+ windowPos.flags &= mask;
+ }
+}
+
+void
+DeferredWindowPosMessage::Run()
+{
+ AssertWindowIsNotNeutered(windowPos.hwnd);
+ if (!IsWindow(windowPos.hwnd)) {
+ NS_ERROR("Invalid window!");
+ return;
+ }
+
+ if (!IsWindow(windowPos.hwndInsertAfter)) {
+ NS_WARNING("ZOrder change cannot be honored");
+ windowPos.hwndInsertAfter = 0;
+ windowPos.flags |= SWP_NOZORDER;
+ }
+
+#ifdef DEBUG
+ BOOL ret =
+#endif
+ SetWindowPos(windowPos.hwnd, windowPos.hwndInsertAfter, windowPos.x,
+ windowPos.y, windowPos.cx, windowPos.cy, windowPos.flags);
+ NS_ASSERTION(ret, "SetWindowPos failed!");
+}
+
+DeferredCopyDataMessage::DeferredCopyDataMessage(HWND aHWnd,
+ UINT aMessage,
+ WPARAM aWParam,
+ LPARAM aLParam)
+: DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
+{
+ NS_ASSERTION(IsWindow(reinterpret_cast<HWND>(aWParam)), "Bad window!");
+
+ COPYDATASTRUCT* source = reinterpret_cast<COPYDATASTRUCT*>(aLParam);
+ NS_ASSERTION(source, "Should never be null!");
+
+ copyData.dwData = source->dwData;
+ copyData.cbData = source->cbData;
+
+ if (source->cbData) {
+ copyData.lpData = malloc(source->cbData);
+ if (copyData.lpData) {
+ memcpy(copyData.lpData, source->lpData, source->cbData);
+ }
+ else {
+ NS_ERROR("Out of memory?!");
+ copyData.cbData = 0;
+ }
+ }
+ else {
+ copyData.lpData = nullptr;
+ }
+
+ lParam = reinterpret_cast<LPARAM>(&copyData);
+}
+
+DeferredCopyDataMessage::~DeferredCopyDataMessage()
+{
+ free(copyData.lpData);
+}
+
+DeferredStyleChangeMessage::DeferredStyleChangeMessage(HWND aHWnd,
+ WPARAM aWParam,
+ LPARAM aLParam)
+: hWnd(aHWnd)
+{
+ index = static_cast<int>(aWParam);
+ style = reinterpret_cast<STYLESTRUCT*>(aLParam)->styleNew;
+}
+
+void
+DeferredStyleChangeMessage::Run()
+{
+ SetWindowLongPtr(hWnd, index, style);
+}
+
+DeferredSetIconMessage::DeferredSetIconMessage(HWND aHWnd,
+ UINT aMessage,
+ WPARAM aWParam,
+ LPARAM aLParam)
+: DeferredSendMessage(aHWnd, aMessage, aWParam, aLParam)
+{
+ NS_ASSERTION(aMessage == WM_SETICON, "Wrong message type!");
+}
+
+void
+DeferredSetIconMessage::Run()
+{
+ AssertWindowIsNotNeutered(hWnd);
+ if (!IsWindow(hWnd)) {
+ NS_ERROR("Invalid window!");
+ return;
+ }
+
+ WNDPROC wndproc =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
+ if (!wndproc) {
+ NS_ERROR("Invalid window procedure!");
+ return;
+ }
+
+ HICON hOld = reinterpret_cast<HICON>(
+ CallWindowProc(wndproc, hWnd, message, wParam, lParam));
+ if (hOld) {
+ DestroyIcon(hOld);
+ }
+}
diff --git a/ipc/glue/WindowsMessageLoop.h b/ipc/glue/WindowsMessageLoop.h
new file mode 100644
index 000000000..80577a712
--- /dev/null
+++ b/ipc/glue/WindowsMessageLoop.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef IPC_GLUE_WINDOWSMESSAGELOOP_H
+#define IPC_GLUE_WINDOWSMESSAGELOOP_H
+
+// This file is only meant to compile on windows
+#include <windows.h>
+
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace ipc {
+namespace windows {
+
+void InitUIThread();
+
+class DeferredMessage
+{
+public:
+ DeferredMessage()
+ {
+ MOZ_COUNT_CTOR(DeferredMessage);
+ }
+
+ virtual ~DeferredMessage()
+ {
+ MOZ_COUNT_DTOR(DeferredMessage);
+ }
+
+ virtual void Run() = 0;
+};
+
+// Uses CallWndProc to deliver a message at a later time. Messages faked with
+// this class must not have pointers in their wParam or lParam members as they
+// may be invalid by the time the message actually runs.
+class DeferredSendMessage : public DeferredMessage
+{
+public:
+ DeferredSendMessage(HWND aHWnd,
+ UINT aMessage,
+ WPARAM aWParam,
+ LPARAM aLParam)
+ : hWnd(aHWnd),
+ message(aMessage),
+ wParam(aWParam),
+ lParam(aLParam)
+ { }
+
+ virtual void Run();
+
+protected:
+ HWND hWnd;
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+};
+
+// Uses RedrawWindow to fake several painting-related messages. Flags passed
+// to the constructor go directly to RedrawWindow.
+class DeferredRedrawMessage : public DeferredMessage
+{
+public:
+ DeferredRedrawMessage(HWND aHWnd,
+ UINT aFlags)
+ : hWnd(aHWnd),
+ flags(aFlags)
+ { }
+
+ virtual void Run();
+
+private:
+ HWND hWnd;
+ UINT flags;
+};
+
+// Uses UpdateWindow to generate a WM_PAINT message if needed.
+class DeferredUpdateMessage : public DeferredMessage
+{
+public:
+ DeferredUpdateMessage(HWND aHWnd);
+
+ virtual void Run();
+
+private:
+ HWND mWnd;
+ RECT mUpdateRect;
+};
+
+// This class duplicates a string that may exist in the lParam member of the
+// message.
+class DeferredSettingChangeMessage : public DeferredSendMessage
+{
+public:
+ DeferredSettingChangeMessage(HWND aHWnd,
+ UINT aMessage,
+ WPARAM aWParam,
+ LPARAM aLParam);
+
+ ~DeferredSettingChangeMessage();
+private:
+ wchar_t* lParamString;
+};
+
+// This class uses SetWindowPos to fake various size-related messages. Flags
+// passed to the constructor go straight through to SetWindowPos.
+class DeferredWindowPosMessage : public DeferredMessage
+{
+public:
+ DeferredWindowPosMessage(HWND aHWnd,
+ LPARAM aLParam,
+ bool aForCalcSize = false,
+ WPARAM aWParam = 0);
+
+ virtual void Run();
+
+private:
+ WINDOWPOS windowPos;
+};
+
+// This class duplicates a data buffer for a WM_COPYDATA message.
+class DeferredCopyDataMessage : public DeferredSendMessage
+{
+public:
+ DeferredCopyDataMessage(HWND aHWnd,
+ UINT aMessage,
+ WPARAM aWParam,
+ LPARAM aLParam);
+
+ ~DeferredCopyDataMessage();
+private:
+ COPYDATASTRUCT copyData;
+};
+
+class DeferredStyleChangeMessage : public DeferredMessage
+{
+public:
+ DeferredStyleChangeMessage(HWND aHWnd,
+ WPARAM aWParam,
+ LPARAM aLParam);
+
+ virtual void Run();
+
+private:
+ HWND hWnd;
+ int index;
+ LONG_PTR style;
+};
+
+class DeferredSetIconMessage : public DeferredSendMessage
+{
+public:
+ DeferredSetIconMessage(HWND aHWnd,
+ UINT aMessage,
+ WPARAM aWParam,
+ LPARAM aLParam);
+
+ virtual void Run();
+};
+
+} /* namespace windows */
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* IPC_GLUE_WINDOWSMESSAGELOOP_H */
diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build
new file mode 100644
index 000000000..dd3a2e1ba
--- /dev/null
+++ b/ipc/glue/moz.build
@@ -0,0 +1,207 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'nsIIPCBackgroundChildCreateCallback.h',
+ 'nsIIPCSerializableInputStream.h',
+ 'nsIIPCSerializableURI.h',
+]
+
+EXPORTS.mozilla.ipc += [
+ 'BackgroundChild.h',
+ 'BackgroundParent.h',
+ 'BackgroundUtils.h',
+ 'BrowserProcessSubThread.h',
+ 'CrashReporterClient.h',
+ 'CrashReporterHost.h',
+ 'CrashReporterMetadataShmem.h',
+ 'CrossProcessMutex.h',
+ 'FileDescriptor.h',
+ 'FileDescriptorSetChild.h',
+ 'FileDescriptorSetParent.h',
+ 'FileDescriptorUtils.h',
+ 'GeckoChildProcessHost.h',
+ 'InputStreamUtils.h',
+ 'IOThreadChild.h',
+ 'IPCStreamUtils.h',
+ 'MessageChannel.h',
+ 'MessageLink.h',
+ 'Neutering.h',
+ 'ProcessChild.h',
+ 'ProtocolUtils.h',
+ 'ScopedXREEmbed.h',
+ 'SendStream.h',
+ 'SendStreamAlloc.h',
+ 'SharedMemory.h',
+ 'SharedMemoryBasic.h',
+ 'Shmem.h',
+ 'TaskFactory.h',
+ 'Transport.h',
+ 'URIUtils.h',
+ 'WindowsMessageLoop.h',
+]
+
+if CONFIG['MOZ_FAULTY'] == '1':
+ EXPORTS.mozilla.ipc += ['Faulty.h']
+ SOURCES += ['Faulty.cpp']
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ DEFINES['WEBRTC_WIN'] = True
+ EXPORTS.mozilla.ipc += [
+ 'Transport_win.h',
+ ]
+ SOURCES += [
+ 'SharedMemory_windows.cpp',
+ 'Transport_win.cpp',
+ 'WindowsMessageLoop.cpp',
+ ]
+else:
+ DEFINES['WEBRTC_POSIX'] = True
+ EXPORTS.mozilla.ipc += [
+ 'Transport_posix.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'SharedMemory_posix.cpp',
+ 'Transport_posix.cpp',
+ ]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ SOURCES += [
+ 'CrossProcessMutex_windows.cpp',
+ ]
+elif not CONFIG['OS_ARCH'] in ('NetBSD', 'OpenBSD'):
+ UNIFIED_SOURCES += [
+ 'CrossProcessMutex_posix.cpp',
+ ]
+else:
+ UNIFIED_SOURCES += [
+ 'CrossProcessMutex_unimplemented.cpp',
+ ]
+
+# Android has its own,
+# almost-but-not-quite-compatible-with-POSIX-or-/dev/shm shared memory
+# impl.
+if CONFIG['OS_TARGET'] == 'Android':
+ EXPORTS.mozilla.ipc += ['SharedMemoryBasic_android.h']
+ UNIFIED_SOURCES += [
+ 'SharedMemoryBasic_android.cpp',
+ ]
+elif CONFIG['OS_ARCH'] == 'Darwin':
+ EXPORTS.mozilla.ipc += ['SharedMemoryBasic_mach.h']
+ SOURCES += [
+ 'SharedMemoryBasic_mach.mm',
+ ]
+else:
+ EXPORTS.mozilla.ipc += ['SharedMemoryBasic_chromium.h']
+
+if CONFIG['OS_ARCH'] == 'Linux':
+ UNIFIED_SOURCES += [
+ 'ProcessUtils_linux.cpp',
+ ]
+elif CONFIG['OS_ARCH'] in ('DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD'):
+ UNIFIED_SOURCES += [
+ 'ProcessUtils_bsd.cpp'
+ ]
+elif CONFIG['OS_ARCH'] == 'Darwin':
+ UNIFIED_SOURCES += [
+ 'ProcessUtils_mac.mm'
+ ]
+else:
+ UNIFIED_SOURCES += [
+ 'ProcessUtils_none.cpp',
+ ]
+
+EXPORTS.ipc += [
+ 'IPCMessageUtils.h',
+]
+
+UNIFIED_SOURCES += [
+ 'BackgroundImpl.cpp',
+ 'BackgroundUtils.cpp',
+ 'BrowserProcessSubThread.cpp',
+ 'CrashReporterClient.cpp',
+ 'CrashReporterHost.cpp',
+ 'CrashReporterMetadataShmem.cpp',
+ 'FileDescriptor.cpp',
+ 'FileDescriptorUtils.cpp',
+ 'InputStreamUtils.cpp',
+ 'IPCMessageUtils.cpp',
+ 'IPCStreamUtils.cpp',
+ 'MessageChannel.cpp',
+ 'MessageLink.cpp',
+ 'MessagePump.cpp',
+ 'ProcessChild.cpp',
+ 'ProtocolUtils.cpp',
+ 'ScopedXREEmbed.cpp',
+ 'SendStreamChild.cpp',
+ 'SendStreamParent.cpp',
+ 'SharedMemory.cpp',
+ 'Shmem.cpp',
+ 'StringUtil.cpp',
+]
+
+# GeckoChildProcessHost.cpp cannot be built in unified mode because it uses plarena.h.
+# URIUtils.cpp cannot be built in unified mode because of name clashes on strdup.
+SOURCES += [
+ 'BackgroundChildImpl.cpp',
+ 'BackgroundParentImpl.cpp',
+ 'FileDescriptorSetChild.cpp',
+ 'FileDescriptorSetParent.cpp',
+ 'GeckoChildProcessHost.cpp',
+ 'URIUtils.cpp',
+]
+
+if CONFIG['_MSC_VER']:
+ # This is intended as a temporary hack to support building with VS2015.
+ # 'reinterpret_cast': conversion from 'DWORD' to 'HANDLE' of greater size
+ SOURCES['BackgroundChildImpl.cpp'].flags += ['-wd4312']
+ SOURCES['BackgroundParentImpl.cpp'].flags += ['-wd4312']
+
+LOCAL_INCLUDES += [
+ '/caps',
+ '/dom/broadcastchannel',
+ '/dom/indexedDB',
+ '/dom/workers',
+ '/media/webrtc/trunk',
+ '/xpcom/build',
+]
+
+IPDL_SOURCES = [
+ 'InputStreamParams.ipdlh',
+ 'IPCStream.ipdlh',
+ 'PBackground.ipdl',
+ 'PBackgroundSharedTypes.ipdlh',
+ 'PBackgroundTest.ipdl',
+ 'PFileDescriptorSet.ipdl',
+ 'ProtocolTypes.ipdlh',
+ 'PSendStream.ipdl',
+ 'URIParams.ipdlh',
+]
+
+LOCAL_INCLUDES += [
+ '/dom/ipc',
+ '/toolkit/crashreporter',
+ '/toolkit/xre',
+ '/xpcom/threads',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+for var in ('MOZ_CHILD_PROCESS_NAME', 'MOZ_CHILD_PROCESS_NAME_PIE',
+ 'MOZ_CHILD_PROCESS_BUNDLE', 'DLL_PREFIX', 'DLL_SUFFIX'):
+ DEFINES[var] = '"%s"' % CONFIG[var]
+
+if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
+ LOCAL_INCLUDES += [
+ '/security/sandbox/chromium',
+ '/security/sandbox/chromium-shim',
+ '/security/sandbox/win/src/sandboxbroker',
+ ]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-shadow']
diff --git a/ipc/glue/nsIIPCBackgroundChildCreateCallback.h b/ipc/glue/nsIIPCBackgroundChildCreateCallback.h
new file mode 100644
index 000000000..5522fa75e
--- /dev/null
+++ b/ipc/glue/nsIIPCBackgroundChildCreateCallback.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_nsiipcbackgroundchildcreatecallback_h
+#define mozilla_ipc_nsiipcbackgroundchildcreatecallback_h
+
+#include "mozilla/Attributes.h"
+#include "nsISupports.h"
+
+namespace mozilla {
+namespace ipc {
+
+class PBackgroundChild;
+
+} // namespace ipc
+} // namespace mozilla
+
+#define NS_IIPCBACKGROUNDCHILDCREATECALLBACK_IID \
+ {0x4de01707, 0x70e3, 0x4181, {0xbc, 0x9f, 0xa3, 0xec, 0xfe, 0x74, 0x1a, 0xe3}}
+
+class NS_NO_VTABLE nsIIPCBackgroundChildCreateCallback : public nsISupports
+{
+public:
+ typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IIPCBACKGROUNDCHILDCREATECALLBACK_IID)
+
+ // This will be called upon successful creation of a PBackgroundChild actor.
+ // The actor is unique per-thread and must not be shared across threads. It
+ // may be saved and reused on the same thread for as long as the thread lives.
+ // After this callback BackgroundChild::GetForCurrentThread() will return the
+ // same actor.
+ virtual void
+ ActorCreated(PBackgroundChild*) = 0;
+
+ // This will be called if for some reason the PBackgroundChild actor cannot be
+ // created. This should never be called in child processes as the failure to
+ // create the actor should result in the termination of the child process
+ // first. This may be called for cross-thread actors in the main process.
+ virtual void
+ ActorFailed() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIIPCBackgroundChildCreateCallback,
+ NS_IIPCBACKGROUNDCHILDCREATECALLBACK_IID)
+
+#define NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK \
+ virtual void \
+ ActorCreated(mozilla::ipc::PBackgroundChild*) override; \
+ virtual void \
+ ActorFailed() override;
+
+#define NS_FORWARD_NSIIPCBACKGROUNDCHILDCREATECALLBACK(_to) \
+ virtual void \
+ ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override \
+ { _to ActorCreated(aActor); } \
+ virtual void \
+ ActorFailed() override \
+ { _to ActorFailed(); }
+
+#define NS_FORWARD_SAFE_NSIIPCBACKGROUNDCHILDCREATECALLBACK(_to) \
+ virtual void \
+ ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override \
+ { if (_to) { _to->ActorCreated(aActor); } } \
+ virtual void \
+ ActorFailed() override \
+ { if (_to) { _to->ActorFailed(); } }
+
+#endif // mozilla_ipc_nsiipcbackgroundchildcreatecallback_h
diff --git a/ipc/glue/nsIIPCSerializableInputStream.h b/ipc/glue/nsIIPCSerializableInputStream.h
new file mode 100644
index 000000000..365e490b1
--- /dev/null
+++ b/ipc/glue/nsIIPCSerializableInputStream.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_nsIIPCSerializableInputStream_h
+#define mozilla_ipc_nsIIPCSerializableInputStream_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "nsISupports.h"
+#include "nsTArrayForwardDeclare.h"
+
+namespace mozilla {
+namespace ipc {
+
+class FileDescriptor;
+class InputStreamParams;
+
+} // namespace ipc
+} // namespace mozilla
+
+#define NS_IIPCSERIALIZABLEINPUTSTREAM_IID \
+ {0xb0211b14, 0xea6d, 0x40d4, {0x87, 0xb5, 0x7b, 0xe3, 0xdf, 0xac, 0x09, 0xd1}}
+
+class NS_NO_VTABLE nsIIPCSerializableInputStream : public nsISupports
+{
+public:
+ typedef nsTArray<mozilla::ipc::FileDescriptor>
+ FileDescriptorArray;
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IIPCSERIALIZABLEINPUTSTREAM_IID)
+
+ virtual void
+ Serialize(mozilla::ipc::InputStreamParams& aParams,
+ FileDescriptorArray& aFileDescriptors) = 0;
+
+ virtual bool
+ Deserialize(const mozilla::ipc::InputStreamParams& aParams,
+ const FileDescriptorArray& aFileDescriptors) = 0;
+
+ // The number of bytes that are expected to be written when this
+ // stream is serialized. A value of Some(N) indicates that N bytes
+ // will be written to the IPC buffer, and will be used to decide
+ // upon an optimal transmission mechanism. A value of Nothing
+ // indicates that either serializing this stream will not require
+ // serializing its contents (eg. a file-backed stream, or a stream
+ // backed by an IPC actor), or the length of the stream's contents
+ // cannot be determined.
+ virtual mozilla::Maybe<uint64_t>
+ ExpectedSerializedLength() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIIPCSerializableInputStream,
+ NS_IIPCSERIALIZABLEINPUTSTREAM_IID)
+
+#define NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM \
+ virtual void \
+ Serialize(mozilla::ipc::InputStreamParams&, \
+ FileDescriptorArray&) override; \
+ \
+ virtual bool \
+ Deserialize(const mozilla::ipc::InputStreamParams&, \
+ const FileDescriptorArray&) override; \
+ \
+ virtual mozilla::Maybe<uint64_t> \
+ ExpectedSerializedLength() override;
+
+#define NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(_to) \
+ virtual void \
+ Serialize(mozilla::ipc::InputStreamParams& aParams, \
+ FileDescriptorArray& aFileDescriptors) override \
+ { \
+ _to Serialize(aParams, aFileDescriptors); \
+ } \
+ \
+ virtual bool \
+ Deserialize(const mozilla::ipc::InputStreamParams& aParams, \
+ const FileDescriptorArray& aFileDescriptors) override \
+ { \
+ return _to Deserialize(aParams, aFileDescriptors); \
+ } \
+ \
+ virtual mozilla::Maybe<uint64_t> \
+ ExpectedSerializedLength() override \
+ { \
+ return _to ExpectedSerializedLength(); \
+ }
+
+#define NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(_to) \
+ virtual void \
+ Serialize(mozilla::ipc::InputStreamParams& aParams, \
+ FileDescriptorArray& aFileDescriptors) override \
+ { \
+ if (_to) { \
+ _to->Serialize(aParams, aFileDescriptors); \
+ } \
+ } \
+ \
+ virtual bool \
+ Deserialize(const mozilla::ipc::InputStreamParams& aParams, \
+ const FileDescriptorArray& aFileDescriptors) override \
+ { \
+ return _to ? _to->Deserialize(aParams, aFileDescriptors) : false; \
+ } \
+ \
+ virtual mozilla::Maybe<uint64_t> \
+ ExpectedSerializedLength() override \
+ { \
+ return _to ? _to->ExpectedSerializedLength() : Nothing(); \
+ }
+
+#endif // mozilla_ipc_nsIIPCSerializableInputStream_h
diff --git a/ipc/glue/nsIIPCSerializableURI.h b/ipc/glue/nsIIPCSerializableURI.h
new file mode 100644
index 000000000..2008ce838
--- /dev/null
+++ b/ipc/glue/nsIIPCSerializableURI.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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_ipc_nsIIPCSerializableURI_h
+#define mozilla_ipc_nsIIPCSerializableURI_h
+
+#include "nsISupports.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace ipc {
+class URIParams;
+} // namespace ipc
+} // namespace mozilla
+
+#define NS_IIPCSERIALIZABLEURI_IID \
+ {0xfee3437d, 0x3daf, 0x411f, {0xb0, 0x1d, 0xdc, 0xd4, 0x88, 0x55, 0xe3, 0xd}}
+
+class NS_NO_VTABLE nsIIPCSerializableURI : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IIPCSERIALIZABLEURI_IID)
+
+ virtual void
+ Serialize(mozilla::ipc::URIParams& aParams) = 0;
+
+ virtual bool
+ Deserialize(const mozilla::ipc::URIParams& aParams) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIIPCSerializableURI,
+ NS_IIPCSERIALIZABLEURI_IID)
+
+#define NS_DECL_NSIIPCSERIALIZABLEURI \
+ virtual void \
+ Serialize(mozilla::ipc::URIParams&) override; \
+ virtual bool \
+ Deserialize(const mozilla::ipc::URIParams&) override;
+
+#define NS_FORWARD_NSIIPCSERIALIZABLEURI(_to) \
+ virtual void \
+ Serialize(mozilla::ipc::URIParams& aParams) override \
+ { _to Serialize(aParams); } \
+ virtual bool \
+ Deserialize(const mozilla::ipc::URIParams& aParams) override \
+ { return _to Deserialize(aParams); }
+
+#define NS_FORWARD_SAFE_NSIIPCSERIALIZABLEURI(_to) \
+ virtual void \
+ Serialize(mozilla::ipc::URIParams& aParams) override \
+ { if (_to) { _to->Serialize(aParams); } } \
+ virtual bool \
+ Deserialize(const mozilla::ipc::URIParams& aParams) override \
+ { if (_to) { return _to->Deserialize(aParams); } return false; }
+
+#endif // mozilla_ipc_nsIIPCSerializableURI_h