Combine AsyncChannel, SyncChannel, and RPCChannel into one class (bug 901789, r=cjones,bent).
authorDavid Anderson <danderson@mozilla.com>
Fri, 27 Sep 2013 18:42:08 -0700
changeset 162938 9cc90a4b64758375a2d77f0679fc547f21902f46
parent 162937 840d3d8c39a0a8d4184c2e2cbb7fcfcc7c352f73
child 162939 8cf8b27db6ea8fb04cd82bb296a90d5480fb6372
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones, bent
bugs901789
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Combine AsyncChannel, SyncChannel, and RPCChannel into one class (bug 901789, r=cjones,bent).
CLOBBER
dom/ipc/Blob.cpp
dom/ipc/Blob.h
dom/ipc/ContentParent.cpp
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginMessageUtils.cpp
dom/plugins/ipc/PluginMessageUtils.h
dom/plugins/ipc/PluginModuleChild.cpp
dom/plugins/ipc/PluginModuleChild.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
gfx/layers/ipc/CompositorChild.cpp
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeParent.cpp
ipc/chromium/src/chrome/common/ipc_message.h
ipc/glue/AsyncChannel.h
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
ipc/glue/MessageLink.cpp
ipc/glue/MessageLink.h
ipc/glue/ProtocolUtils.cpp
ipc/glue/ProtocolUtils.h
ipc/glue/RPCChannel.cpp
ipc/glue/RPCChannel.h
ipc/glue/SyncChannel.cpp
ipc/glue/SyncChannel.h
ipc/glue/WindowsMessageLoop.cpp
ipc/glue/moz.build
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/type.py
ipc/ipdl/test/cxx/PTestDataStructures.ipdl
ipc/ipdl/test/cxx/TestBridgeMain.cpp
ipc/ipdl/test/cxx/TestOpens.cpp
ipc/ipdl/test/cxx/TestRPCRaces.cpp
ipc/ipdl/test/cxx/TestRPCRaces.h
ipc/ipdl/test/cxx/TestRaceDeferral.cpp
ipc/ipdl/test/cxx/TestRaceDeferral.h
ipc/ipdl/test/cxx/TestStackHooks.cpp
ipc/ipdl/test/cxx/TestUrgency.cpp
ipc/ipdl/test/cxx/genIPDLUnitTests.py
widget/windows/nsAppShell.cpp
widget/windows/nsWindow.cpp
widget/windows/nsWindowGfx.cpp
widget/xpwidgets/nsBaseWidget.cpp
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 913260 needed a clobber to not break tons of tests 
+Bug 901789 needs a clobber.
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -463,17 +463,17 @@ private:
   }
 };
 
 template <ActorFlavorEnum ActorFlavor>
 inline
 already_AddRefed<nsIDOMBlob>
 GetBlobFromParams(const SlicedBlobConstructorParams& aParams)
 {
-  static_assert(ActorFlavor == mozilla::dom::ipc::Parent,
+  static_assert(ActorFlavor == Parent,
                 "No other flavor is supported here!");
 
   BlobParent* actor =
     const_cast<BlobParent*>(
       static_cast<const BlobParent*>(aParams.sourceParent()));
   MOZ_ASSERT(actor);
 
   return actor->GetBlob();
--- a/dom/ipc/Blob.h
+++ b/dom/ipc/Blob.h
@@ -164,17 +164,17 @@ public:
   typedef typename BlobTraits<ActorFlavor>::StreamType StreamType;
   typedef typename BlobTraits<ActorFlavor>::ConstructorParamsType
           ConstructorParamsType;
   typedef typename BlobTraits<ActorFlavor>::OtherSideConstructorParamsType
           OtherSideConstructorParamsType;
   typedef typename BlobTraits<ActorFlavor>::BaseType BaseType;
   typedef RemoteBlob<ActorFlavor> RemoteBlobType;
   typedef mozilla::ipc::IProtocolManager<
-                      mozilla::ipc::RPCChannel::RPCListener>::ActorDestroyReason
+                      mozilla::ipc::MessageListener>::ActorDestroyReason
           ActorDestroyReason;
 
 protected:
   nsIDOMBlob* mBlob;
   RemoteBlobType* mRemoteBlob;
   bool mOwnsBlob;
   bool mBlobIsFile;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -257,17 +257,17 @@ NS_IMETHODIMP
 ContentParentMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb,
                                             nsISupports* aClosure)
 {
     nsAutoTArray<ContentParent*, 16> cps;
     ContentParent::GetAllEvenIfDead(cps);
 
     for (uint32_t i = 0; i < cps.Length(); i++) {
         ContentParent* cp = cps[i];
-        AsyncChannel* channel = cp->GetIPCChannel();
+        MessageChannel* channel = cp->GetIPCChannel();
 
         nsString friendlyName;
         cp->FriendlyName(friendlyName);
 
         cp->AddRef();
         nsrefcnt refcnt = cp->Release();
 
         const char* channelStr = "no channel";
@@ -874,17 +874,17 @@ ContentParent::ShutDownProcess(bool aClo
     if (!aCloseWithError && !mCalledClose) {
         // Close() can only be called once: It kicks off the destruction
         // sequence.
         mCalledClose = true;
         Close();
     }
 
     if (aCloseWithError && !mCalledCloseWithError) {
-        AsyncChannel* channel = GetIPCChannel();
+        MessageChannel* channel = GetIPCChannel();
         if (channel) {
             mCalledCloseWithError = true;
             channel->CloseWithError();
         }
     }
 
     // NB: must MarkAsDead() here so that this isn't accidentally
     // returned from Get*() while in the midst of shutdown.
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -23,17 +23,17 @@
 extern const PRUnichar* kFlashFullscreenClass;
 using mozilla::gfx::SharedDIBSurface;
 #endif
 #include "gfxSharedImageSurface.h"
 #include "gfxUtils.h"
 #include "gfxAlphaRecovery.h"
 
 #include "mozilla/Util.h"
-#include "mozilla/ipc/SyncChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/AutoRestore.h"
 
 using namespace mozilla;
 using mozilla::ipc::ProcessChild;
 using namespace mozilla::plugins;
 using namespace mozilla::layers;
 using namespace std;
 
@@ -1400,17 +1400,17 @@ PluginInstanceChild::PluginWindowProc(HW
 
 // static
 LRESULT CALLBACK
 PluginInstanceChild::PluginWindowProcInternal(HWND hWnd,
                                               UINT message,
                                               WPARAM wParam,
                                               LPARAM lParam)
 {
-    NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(),
+    NS_ASSERTION(!mozilla::ipc::MessageChannel::IsPumpingMessages(),
                  "Failed to prevent a nonqueued message from running!");
     PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
         GetProp(hWnd, kPluginInstanceChildProperty));
     if (!self) {
         NS_NOTREACHED("Badness!");
         return 0;
     }
 
--- a/dom/plugins/ipc/PluginMessageUtils.cpp
+++ b/dom/plugins/ipc/PluginMessageUtils.cpp
@@ -10,17 +10,17 @@
 
 #include "PluginInstanceParent.h"
 #include "PluginInstanceChild.h"
 #include "PluginScriptableObjectParent.h"
 #include "PluginScriptableObjectChild.h"
 
 using std::string;
 
-using mozilla::ipc::RPCChannel;
+using mozilla::ipc::MessageChannel;
 
 namespace {
 
 class DeferNPObjectReleaseRunnable : public nsRunnable
 {
 public:
   DeferNPObjectReleaseRunnable(const NPNetscapeFuncs* f, NPObject* o)
     : mFuncs(f)
@@ -62,31 +62,31 @@ NPRemoteWindow::NPRemoteWindow() :
 #endif
 {
   clipRect.top = 0;
   clipRect.left = 0;
   clipRect.bottom = 0;
   clipRect.right = 0;
 }
 
-RPCChannel::RacyRPCPolicy
-MediateRace(const RPCChannel::Message& parent,
-            const RPCChannel::Message& child)
+ipc::RacyRPCPolicy
+MediateRace(const MessageChannel::Message& parent,
+            const MessageChannel::Message& child)
 {
   switch (parent.type()) {
   case PPluginInstance::Msg_Paint__ID:
   case PPluginInstance::Msg_NPP_SetWindow__ID:
   case PPluginInstance::Msg_NPP_HandleEvent_Shmem__ID:
   case PPluginInstance::Msg_NPP_HandleEvent_IOSurface__ID:
     // our code relies on the frame list not changing during paints and
     // reflows
-    return RPCChannel::RRPParentWins;
+    return ipc::RRPParentWins;
 
   default:
-    return RPCChannel::RRPChildWins;
+    return ipc::RRPChildWins;
   }
 }
 
 #if defined(OS_LINUX)
 static string
 ReplaceAll(const string& haystack, const string& needle, const string& with)
 {
   string munged = haystack;
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_PLUGINS_PLUGINMESSAGEUTILS_H
 #define DOM_PLUGINS_PLUGINMESSAGEUTILS_H
 
 #include "ipc/IPCMessageUtils.h"
 #include "base/message_loop.h"
 
-#include "mozilla/ipc/RPCChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/CrossProcessMutex.h"
 #include "gfxipc/ShadowLayerUtils.h"
 
 #include "npapi.h"
 #include "npruntime.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
@@ -38,19 +38,19 @@ namespace plugins {
 using layers::SurfaceDescriptorX11;
 
 enum ScriptableObjectType
 {
   LocalObject,
   Proxy
 };
 
-mozilla::ipc::RPCChannel::RacyRPCPolicy
-MediateRace(const mozilla::ipc::RPCChannel::Message& parent,
-            const mozilla::ipc::RPCChannel::Message& child);
+mozilla::ipc::RacyRPCPolicy
+MediateRace(const mozilla::ipc::MessageChannel::Message& parent,
+            const mozilla::ipc::MessageChannel::Message& child);
 
 std::string
 MungePluginDsoPath(const std::string& path);
 std::string
 UnmungePluginDsoPath(const std::string& munged);
 
 extern PRLogModuleInfo* GetPluginLog();
 
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -11,17 +11,17 @@
 #include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleChild.h"
 
 /* This must occur *after* plugins/PluginModuleChild.h to avoid typedefs conflicts. */
 #include "mozilla/Util.h"
 
-#include "mozilla/ipc/SyncChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 
 #ifdef MOZ_WIDGET_GTK
 #include <gtk/gtk.h>
 #if (MOZ_WIDGET_GTK == 3)
 #include <gtk/gtkx.h>
 #endif
 #endif
 
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -69,17 +69,17 @@ static const int kNestedLoopDetectorInte
 
 class PluginScriptableObjectChild;
 class PluginInstanceChild;
 
 class PluginModuleChild : public PPluginModuleChild
 {
     typedef mozilla::dom::PCrashReporterChild PCrashReporterChild;
 protected:
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
     // Implement the PPluginModuleChild interface
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -11,17 +11,17 @@
 #include "NestedLoopTimer.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/PCrashReporterParent.h"
-#include "mozilla/ipc/SyncChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #include "nsIFile.h"
@@ -46,17 +46,17 @@
 #elif XP_MACOSX
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 using base::KillProcess;
 
 using mozilla::PluginLibrary;
-using mozilla::ipc::SyncChannel;
+using mozilla::ipc::MessageChannel;
 using mozilla::dom::PCrashReporterParent;
 using mozilla::dom::CrashReporterParent;
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
 #ifdef MOZ_CRASHREPORTER
@@ -243,17 +243,17 @@ PluginModuleParent::WriteExtraDataForMin
     }
 }
 #endif  // MOZ_CRASHREPORTER
 
 void
 PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
 {
     int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
-                      SyncChannel::kNoTimeout;
+                      MessageChannel::kNoTimeout;
     SetReplyTimeoutMs(timeoutMs);
 }
 
 int
 PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 #ifndef XP_WIN
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -129,17 +129,17 @@ public:
     void TerminateChildProcess(MessageLoop* aMsgLoop);
 
 #ifdef XP_WIN
     void
     ExitedCxxStack() MOZ_OVERRIDE;
 #endif // XP_WIN
 
 protected:
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual bool RecvXXX_HACK_FIXME_cjones(Shmem& mem) { NS_RUNTIMEABORT("not reached"); return false; }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -59,18 +59,17 @@ CompositorChild::Create(Transport* aTran
 
   nsRefPtr<CompositorChild> child(new CompositorChild(nullptr));
   ProcessHandle handle;
   if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
     // We can't go on without a compositor.
     NS_RUNTIMEABORT("Couldn't OpenProcessHandle() to parent process.");
     return false;
   }
-  if (!child->Open(aTransport, handle, XRE_GetIOMessageLoop(),
-                AsyncChannel::Child)) {
+  if (!child->Open(aTransport, handle, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
     NS_RUNTIMEABORT("Couldn't Open() Compositor channel.");
     return false;
   }
   // We release this ref in ActorDestroy().
   sCompositor = child.forget().get();
   return true;
 }
 
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -15,17 +15,17 @@
 #include "base/process.h"               // for ProcessHandle
 #include "base/process_util.h"          // for OpenProcessHandle
 #include "base/task.h"                  // for NewRunnableFunction, etc
 #include "base/thread.h"                // for Thread
 #include "base/tracked.h"               // for FROM_HERE
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Monitor.h"            // for Monitor, MonitorAutoLock
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitor, etc
-#include "mozilla/ipc/AsyncChannel.h"   // for AsyncChannel, etc
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild, etc
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "mozilla/layers/LayersMessages.h"  // for CompositableOperation
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/mozalloc.h"           // for operator new, etc
@@ -284,18 +284,18 @@ static void DeallocSurfaceDescriptorGral
   *aDone = true;
   aBarrier->NotifyAll();
 }
 
 // dispatched function
 static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent)
 {
   MessageLoop *parentMsgLoop = parent->GetMessageLoop();
-  ipc::AsyncChannel *parentChannel = parent->GetIPCChannel();
-  child->Open(parentChannel, parentMsgLoop, mozilla::ipc::AsyncChannel::Child);
+  ipc::MessageChannel *parentChannel = parent->GetIPCChannel();
+  child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
 }
 
 ImageBridgeChild::ImageBridgeChild()
 {
   mTxn = new CompositableTransaction();
 }
 ImageBridgeChild::~ImageBridgeChild()
 {
@@ -353,17 +353,17 @@ void ImageBridgeChild::StartUp()
 
 static void
 ConnectImageBridgeInChildProcess(Transport* aTransport,
                                  ProcessHandle aOtherProcess)
 {
   // Bind the IPC channel to the image bridge thread.
   sImageBridgeChildSingleton->Open(aTransport, aOtherProcess,
                                    XRE_GetIOMessageLoop(),
-                                   AsyncChannel::Child);
+                                   ipc::ChildSide);
 }
 
 static void ReleaseImageClientNow(ImageClient* aClient)
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   aClient->Release();
 }
 
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -7,17 +7,17 @@
 #include <stdint.h>                     // for uint64_t, uint32_t
 #include "CompositableHost.h"           // for CompositableParent, Create
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/process.h"               // for ProcessHandle
 #include "base/process_util.h"          // for OpenProcessHandle
 #include "base/task.h"                  // for CancelableTask, DeleteTask, etc
 #include "base/tracked.h"               // for FROM_HERE
 #include "gfxPoint.h"                   // for gfxIntSize
-#include "mozilla/ipc/AsyncChannel.h"   // for AsyncChannel, etc
+#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/CompositableTransactionParent.h"
 #include "mozilla/layers/CompositorParent.h"  // for CompositorParent
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply
 #include "mozilla/layers/LayersSurfaces.h"  // for PGrallocBufferParent
 #include "mozilla/layers/PCompositableParent.h"
@@ -94,18 +94,17 @@ ImageBridgeParent::RecvUpdateNoSwap(cons
 }
 
 
 static void
 ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge,
                                   Transport* aTransport,
                                   ProcessHandle aOtherProcess)
 {
-  aBridge->Open(aTransport, aOtherProcess,
-                XRE_GetIOMessageLoop(), AsyncChannel::Parent);
+  aBridge->Open(aTransport, aOtherProcess, XRE_GetIOMessageLoop(), ipc::ParentSide);
 }
 
 /*static*/ bool
 ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess)
 {
   ProcessHandle processHandle;
   if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
     return false;
--- a/ipc/chromium/src/chrome/common/ipc_message.h
+++ b/ipc/chromium/src/chrome/common/ipc_message.h
@@ -86,16 +86,21 @@ class Message : public Pickle {
     return (header()->flags & SYNC_BIT) != 0;
   }
 
   // True if this is a synchronous message.
   bool is_rpc() const {
     return (header()->flags & RPC_BIT) != 0;
   }
 
+  // True if this is an urgent message.
+  bool is_urgent() const {
+    return (header()->flags & URGENT_BIT) != 0;
+  }
+
   // True if compression is enabled for this message.
   bool compress() const {
     return (header()->flags & COMPRESS_BIT) != 0;
   }
 
   // Set this on a reply to a synchronous message.
   void set_reply() {
     header()->flags |= REPLY_BIT;
@@ -265,31 +270,36 @@ class Message : public Pickle {
   void set_sync() {
     header()->flags |= SYNC_BIT;
   }
 
   void set_rpc() {
     header()->flags |= RPC_BIT;
   }
 
+  void set_urgent() {
+    header()->flags |= URGENT_BIT;
+  }
+
 #if !defined(OS_MACOSX)
  protected:
 #endif
 
   // flags
   enum {
     PRIORITY_MASK   = 0x0003,
     SYNC_BIT        = 0x0004,
     REPLY_BIT       = 0x0008,
     REPLY_ERROR_BIT = 0x0010,
     UNBLOCK_BIT     = 0x0020,
     PUMPING_MSGS_BIT= 0x0040,
     HAS_SENT_TIME_BIT = 0x0080,
     RPC_BIT         = 0x0100,
-    COMPRESS_BIT    = 0x0200
+    COMPRESS_BIT    = 0x0200,
+    URGENT_BIT      = 0x0400
   };
 
   struct Header : Pickle::Header {
     int32_t routing;  // ID of the view that this message is destined for
     msgid_t type;   // specifies the user-defined message type
     uint32_t flags;   // specifies control flags for the message
 #if defined(OS_POSIX)
     uint32_t num_fds; // the number of descriptors included with this message
deleted file mode 100644
--- a/ipc/glue/AsyncChannel.h
+++ /dev/null
@@ -1,298 +0,0 @@
-/* -*- 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_AsyncChannel_h
-#define ipc_glue_AsyncChannel_h 1
-
-#include "base/basictypes.h"
-#include "base/message_loop.h"
-
-#include "mozilla/WeakPtr.h"
-#include "mozilla/Monitor.h"
-#include "mozilla/ipc/Transport.h"
-#include "nsAutoPtr.h"
-
-//-----------------------------------------------------------------------------
-
-namespace mozilla {
-namespace ipc {
-
-struct HasResultCodes
-{
-    enum Result {
-        MsgProcessed,
-        MsgDropped,
-        MsgNotKnown,
-        MsgNotAllowed,
-        MsgPayloadError,
-        MsgProcessingError,
-        MsgRouteError,
-        MsgValueError
-    };
-};
-
-
-class RefCountedMonitor : public Monitor
-{
-public:
-    RefCountedMonitor() 
-        : Monitor("mozilla.ipc.AsyncChannel.mMonitor")
-    {}
-
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMonitor)
-};
-
-class AsyncChannel : protected HasResultCodes
-{
-protected:
-    typedef mozilla::Monitor Monitor;
-
-    enum ChannelState {
-        ChannelClosed,
-        ChannelOpening,
-        ChannelConnected,
-        ChannelTimeout,
-        ChannelClosing,
-        ChannelError
-    };
-
-public:
-    typedef IPC::Message Message;
-    typedef mozilla::ipc::Transport Transport;
-
-    class /*NS_INTERFACE_CLASS*/ AsyncListener
-        : protected HasResultCodes
-        , public mozilla::SupportsWeakPtr<AsyncListener>
-    {
-    public:
-        virtual ~AsyncListener() { }
-
-        virtual void OnChannelClose() = 0;
-        virtual void OnChannelError() = 0;
-        virtual Result OnMessageReceived(const Message& aMessage) = 0;
-        virtual void OnProcessingError(Result aError) = 0;
-        // FIXME/bug 792652: this doesn't really belong here, but a
-        // large refactoring is needed to put it where it belongs.
-        virtual int32_t GetProtocolTypeId() = 0;
-        virtual void OnChannelConnected(int32_t peer_pid) {}
-    };
-
-    enum Side { Parent, Child, Unknown };
-
-public:
-    //
-    // These methods are called on the "worker" thread
-    //
-    AsyncChannel(AsyncListener* aListener);
-    virtual ~AsyncChannel();
-
-    // "Open" from the perspective of the transport layer; the underlying
-    // socketpair/pipe should already be created.
-    //
-    // Returns true iff the transport layer was successfully connected,
-    // i.e., mChannelState == ChannelConnected.
-    bool Open(Transport* aTransport, MessageLoop* aIOLoop=0, Side aSide=Unknown);
-    
-    // "Open" a connection to another thread in the same process.
-    //
-    // Returns true iff 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 AsyncChannel.cpp.
-    bool Open(AsyncChannel *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();
-
-    // Asynchronously send a message to the other side of the channel
-    virtual bool Send(Message* msg);
-
-    // Asynchronously deliver a message back to this side of the
-    // channel
-    virtual bool Echo(Message* msg);
-
-    // Send OnChannelConnected notification to listeners.
-    void DispatchOnChannelConnected(int32_t peer_pid);
-
-    // 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;
-    uint32_t Unsound_NumQueuedMessages() const;
-
-    //
-    // Each AsyncChannel is associated with either a ProcessLink or a
-    // ThreadLink via the field mLink.  The type of link is determined
-    // by whether this AsyncChannel is communicating with another
-    // process or another thread.  In the former case, file
-    // descriptors or a socket are used via the I/O queue.  In the
-    // latter case, messages are enqueued directly onto the target
-    // thread's work queue.
-    //
-
-    class Link {
-    protected:
-        AsyncChannel *mChan;
-
-    public:
-        Link(AsyncChannel *aChan);
-        virtual ~Link();
-
-        // 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;
-    };
-
-    class ProcessLink : public Link, public Transport::Listener {
-    protected:
-        Transport* mTransport;
-        MessageLoop* mIOLoop;       // thread where IO happens
-        Transport::Listener* mExistingListener; // channel's previous listener
-    
-        void OnCloseChannel();
-        void OnChannelOpened();
-        void OnTakeConnectedChannel();
-        void OnEchoMessage(Message* msg);
-
-        void AssertIOThread() const
-        {
-            NS_ABORT_IF_FALSE(mIOLoop == MessageLoop::current(),
-                              "not on I/O thread!");
-        }
-
-    public:
-        ProcessLink(AsyncChannel *chan);
-        virtual ~ProcessLink();
-        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(const Message& msg) MOZ_OVERRIDE;
-        virtual void OnChannelConnected(int32_t peer_pid) MOZ_OVERRIDE;
-        virtual void OnChannelError() MOZ_OVERRIDE;
-
-        virtual void EchoMessage(Message *msg) MOZ_OVERRIDE;
-        virtual void SendMessage(Message *msg) MOZ_OVERRIDE;
-        virtual void SendClose() MOZ_OVERRIDE;
-
-        virtual bool Unsound_IsClosed() const MOZ_OVERRIDE;
-        virtual uint32_t Unsound_NumQueuedMessages() const MOZ_OVERRIDE;
-    };
-    
-    class ThreadLink : public Link {
-    protected:
-        AsyncChannel* mTargetChan;
-    
-    public:
-        ThreadLink(AsyncChannel *aChan, AsyncChannel *aTargetChan);
-        virtual ~ThreadLink();
-
-        virtual void EchoMessage(Message *msg) MOZ_OVERRIDE;
-        virtual void SendMessage(Message *msg) MOZ_OVERRIDE;
-        virtual void SendClose() MOZ_OVERRIDE;
-
-        virtual bool Unsound_IsClosed() const MOZ_OVERRIDE;
-        virtual uint32_t Unsound_NumQueuedMessages() const MOZ_OVERRIDE;
-    };
-
-protected:
-    // 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
-    {
-        NS_ABORT_IF_FALSE(mWorkerLoopID != MessageLoop::current()->id(),
-                          "on worker thread but should not be!");
-    }
-
-    // Can be run on either thread
-    void AssertWorkerThread() const
-    {
-        NS_ABORT_IF_FALSE(mWorkerLoopID == MessageLoop::current()->id(),
-                          "not on worker thread!");
-    }
-
-    bool 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);
-    }
-
-    // Return true if |msg| is a special message targeted at the IO
-    // thread, in which case it shouldn't be delivered to the worker.
-    virtual bool MaybeInterceptSpecialIOMessage(const Message& msg);
-    void ProcessGoodbyeMessage();
-
-    // Runs on the link thread. Invoked either from the I/O thread methods above
-    // or directly from the other actor if using a thread-based link.
-    // 
-    // n.b.: mMonitor is always held when these methods are invoked.
-    // In the case of a ProcessLink, it is acquired by the ProcessLink.
-    // In the case of a ThreadLink, it is acquired by the other actor, 
-    // which then invokes these methods directly.
-    virtual void OnMessageReceivedFromLink(const Message& msg);
-    virtual void OnChannelErrorFromLink();
-    void PostErrorNotifyTask();
-
-    // Run on the worker thread
-    void OnDispatchMessage(const Message& aMsg);
-    virtual bool OnSpecialMessage(uint16_t id, const Message& msg);
-    void SendSpecialMessage(Message* msg) const;
-
-    // Tell the IO thread to close the channel and wait for it to ACK.
-    void SynchronouslyClose();
-
-    bool MaybeHandleError(Result code, const char* channelName);
-    void ReportConnectionError(const char* channelName) const;
-
-    // Run on the worker thread
-
-    void OnNotifyMaybeChannelError();
-    virtual bool ShouldDeferNotifyMaybeError() const {
-        return false;
-    }
-    void NotifyChannelClosed();
-    void NotifyMaybeChannelError();
-    void OnOpenAsSlave(AsyncChannel *aTargetChan, Side aSide);
-    void CommonThreadOpenInit(AsyncChannel *aTargetChan, Side aSide);
-
-    virtual void Clear();
-
-    mozilla::WeakPtr<AsyncListener> mListener;
-    ChannelState mChannelState;
-    nsRefPtr<RefCountedMonitor> mMonitor;
-    MessageLoop* mWorkerLoop;   // thread where work is done
-    bool mChild;                // am I the child or parent?
-    CancelableTask* mChannelErrorTask; // NotifyMaybeChannelError runnable
-    Link *mLink;                // link to other thread/process
-
-    // id() of mWorkerLoop.  This persists even after mWorkerLoop is cleared
-    // during channel shutdown.
-    int mWorkerLoopID;
-};
-
-} // namespace ipc
-} // namespace mozilla
-#endif  // ifndef ipc_glue_AsyncChannel_h
rename from ipc/glue/RPCChannel.cpp
rename to ipc/glue/MessageChannel.cpp
--- a/ipc/glue/RPCChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -1,328 +1,907 @@
 /* -*- 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/RPCChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
 #include "nsDebug.h"
 #include "nsTraceRefcnt.h"
 
-#define RPC_ASSERT(_cond, ...)                                      \
-    do {                                                            \
-        if (!(_cond))                                               \
-            DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
-    } while (0)
+using namespace mozilla;
+using namespace std;
 
 using mozilla::MonitorAutoLock;
 using mozilla::MonitorAutoUnlock;
 
 template<>
-struct RunnableMethodTraits<mozilla::ipc::RPCChannel>
+struct RunnableMethodTraits<mozilla::ipc::MessageChannel>
 {
-    static void RetainCallee(mozilla::ipc::RPCChannel* obj) { }
-    static void ReleaseCallee(mozilla::ipc::RPCChannel* obj) { }
+    static void RetainCallee(mozilla::ipc::MessageChannel* obj) { }
+    static void ReleaseCallee(mozilla::ipc::MessageChannel* obj) { }
 };
 
+#define IPC_ASSERT(_cond, ...)                                      \
+    do {                                                            \
+        if (!(_cond))                                               \
+            DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
+    } while (0)
 
 namespace mozilla {
 namespace ipc {
 
-RPCChannel::RPCChannel(RPCListener* aListener)
-  : SyncChannel(aListener),
-    mPending(),
-    mStack(),
-    mOutOfTurnReplies(),
-    mDeferred(),
-    mRemoteStackDepthGuess(0),
+const int32_t MessageChannel::kNoTimeout = INT32_MIN;
+
+// static
+bool MessageChannel::sIsPumpingMessages = false;
+
+MessageChannel::MessageChannel(MessageListener *aListener)
+  : mListener(aListener->asWeakPtr()),
+    mChannelState(ChannelClosed),
+    mSide(UnknownSide),
+    mLink(nullptr),
+    mWorkerLoop(nullptr),
+    mChannelErrorTask(nullptr),
+    mWorkerLoopID(-1),
+    mTimeoutMs(kNoTimeout),
+    mInTimeoutSecondHalf(false),
+    mNextSeqno(0),
+    mPendingSyncReplies(0),
+    mPendingUrgentReplies(0),
+    mDispatchingSyncMessage(false),
+    mRemoteStackDepthGuess(false),
     mSawRPCOutMsg(false)
 {
-    MOZ_COUNT_CTOR(RPCChannel);
+    MOZ_COUNT_CTOR(ipc::MessageChannel);
+
+#ifdef OS_WIN
+    mTopFrame = nullptr;
+#endif
 
     mDequeueOneTask = new RefCountedTask(NewRunnableMethod(
                                                  this,
-                                                 &RPCChannel::OnMaybeDequeueOne));
+                                                 &MessageChannel::OnMaybeDequeueOne));
+
+#ifdef OS_WIN
+    mEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+    NS_ASSERTION(mEvent, "CreateEvent failed! Nothing is going to work!");
+#endif
 }
 
-RPCChannel::~RPCChannel()
+MessageChannel::~MessageChannel()
 {
-    MOZ_COUNT_DTOR(RPCChannel);
-    RPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
-    Clear();
+    MOZ_COUNT_DTOR(ipc::MessageChannel);
+    IPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
+#ifdef OS_WIN
+    CloseHandle(mEvent);
+#endif
+}
+
+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);
 }
 
 void
-RPCChannel::Clear()
+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.
+
     mDequeueOneTask->Cancel();
 
-    AsyncChannel::Clear();
+    mWorkerLoop = nullptr;
+    delete mLink;
+    mLink = nullptr;
+
+    if (mChannelErrorTask) {
+        mChannelErrorTask->Cancel();
+        mChannelErrorTask = nullptr;
+    }
+}
+
+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(
+        FROM_HERE,
+        NewRunnableMethod(aTargetChan, &MessageChannel::OnOpenAsSlave, this, oppSide));
+
+    while (ChannelOpening == mChannelState)
+        mMonitor->Wait();
+    NS_ASSERTION(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);
+    NS_ASSERTION(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();
+    IPC_ASSERT(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
+
+    MonitorAutoLock lock(*mMonitor);
+
+    if (!Connected()) {
+        ReportConnectionError("MessageChannel");
+        return false;
+    }
+
+    mLink->EchoMessage(msg.forget());
+    return true;
+}
+
+bool
+MessageChannel::Send(Message* aMsg)
+{
+    Message copy = *aMsg;
+    CxxStackFrame frame(*this, OUT_MESSAGE, &copy);
+
+    nsAutoPtr<Message> msg(aMsg);
+    AssertWorkerThread();
+    mMonitor->AssertNotCurrentThreadOwns();
+    IPC_ASSERT(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
+
+    MonitorAutoLock lock(*mMonitor);
+    if (!Connected()) {
+        ReportConnectionError("MessageChannel");
+        return false;
+    }
+    mLink->SendMessage(msg.forget());
+    return true;
 }
 
 bool
-RPCChannel::EventOccurred() const
+MessageChannel::MaybeInterceptSpecialIOMessage(const Message& aMsg)
 {
-    AssertWorkerThread();
+    AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
-    RPC_ASSERT(StackDepth() > 0, "not in wait loop");
+
+    if (MSG_ROUTING_NONE == aMsg.routing_id() &&
+        GOODBYE_MESSAGE_TYPE == aMsg.type())
+    {
+        // :TODO: Sort out Close() on this side racing with Close() on the
+        // other side
+        mChannelState = ChannelClosing;
+        printf("NOTE: %s process received `Goodbye', closing down\n",
+               (mSide == ChildSide) ? "child" : "parent");
+        return true;
+    }
+    return false;
+}
+
+void
+MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
+{
+    AssertLinkThread();
+    mMonitor->AssertCurrentThreadOwns();
+
+    if (MaybeInterceptSpecialIOMessage(aMsg))
+        return;
+
+    // Regardless of the RPC stack, if we're awaiting a sync or urgent reply,
+    // we know that it needs to be immediately handled to unblock us.
+    if ((AwaitingSyncReply() && aMsg.is_sync()) ||
+        (AwaitingUrgentReply() && aMsg.is_urgent()))
+    {
+        mRecvd = new Message(aMsg);
+        NotifyWorkerThread();
+        return;
+    }
+
+    // Urgent messages cannot be compressed.
+    MOZ_ASSERT(!aMsg.compress() || !aMsg.is_urgent());
 
-    return (!Connected() ||
-            !mPending.empty() ||
-            !mUrgent.empty() ||
-            (!mOutOfTurnReplies.empty() &&
-             mOutOfTurnReplies.find(mStack.top().seqno())
-             != mOutOfTurnReplies.end()));
+    bool compress = (aMsg.compress() && !mPending.empty() &&
+                     mPending.back().type() == aMsg.type() &&
+                     mPending.back().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_ASSERT(mPending.back().compress());
+        mPending.pop_back();
+    }
+
+    if (aMsg.is_urgent()) {
+        MOZ_ASSERT(!mPendingUrgentRequest);
+        mPendingUrgentRequest = new Message(aMsg);
+    } else {
+        mPending.push_back(aMsg);
+    }
+
+    // There are four 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 high priority, we wake up the main thread to
+    //     deliver the message. 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 RPC 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.
+    //
+    if (AwaitingRPCReply() || (AwaitingSyncReply() && aMsg.is_urgent())) {
+        // Always wake up our RPC waiter, and wake up sync waiters for urgent
+        // messages.
+        NotifyWorkerThread();
+    } else {
+        // Worker thread is either not blocked on a reply, or this is an
+        // incoming RPC that raced with outgoing sync, and needs to be
+        // deferred to a later event-loop iteration.
+        if (!compress) {
+            // If we compressed away the previous message, we'll re-use
+            // its pending task.
+            mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
+        }
+    }
 }
 
 bool
-RPCChannel::Send(Message* msg)
+MessageChannel::Send(Message* aMsg, Message* aReply)
 {
-    Message copy = *msg;
+    // Sanity checks.
+    AssertWorkerThread();
+    mMonitor->AssertNotCurrentThreadOwns();
+
+#ifdef OS_WIN
+    SyncStackFrame frame(this, false);
+#endif
+
+    Message copy = *aMsg;
     CxxStackFrame f(*this, OUT_MESSAGE, &copy);
-    return AsyncChannel::Send(msg);
+
+    MonitorAutoLock lock(*mMonitor);
+
+    IPC_ASSERT(aMsg->is_sync(), "can only Send() sync messages here");
+    IPC_ASSERT(!DispatchingSyncMessage(), "violation of sync handler invariant");
+
+    if (AwaitingSyncReply()) {
+        // This is a temporary hack in place, for e10s CPOWs, until bug 901789
+        // and the new followup RPC protocol land. Eventually this will become
+        // an assert again. See bug 900062 for details.
+        NS_ERROR("Nested sync messages are not supported");
+        return false;
+    }
+
+    AutoEnterPendingReply replies(mPendingSyncReplies);
+    if (!SendAndWait(aMsg, aReply))
+        return false;
+
+    NS_ABORT_IF_FALSE(aReply->is_sync(), "reply is not sync");
+    return true;
+}
+
+bool
+MessageChannel::UrgentCall(Message* aMsg, Message* aReply)
+{
+    AssertWorkerThread();
+    mMonitor->AssertNotCurrentThreadOwns();
+    IPC_ASSERT(mSide == ParentSide, "cannot send urgent requests from child");
+
+#ifdef OS_WIN
+    SyncStackFrame frame(this, false);
+#endif
+
+    Message copy = *aMsg;
+    CxxStackFrame f(*this, OUT_MESSAGE, &copy);
+
+    MonitorAutoLock lock(*mMonitor);
+
+    // At the moment, we don't allow urgent outcalls to nest, though this will
+    // change soon.
+    IPC_ASSERT(!AwaitingUrgentReply(), "urgent calls cannot nest");
+    IPC_ASSERT(!AwaitingRPCReply(), "urgent calls cannot be issued within RPC calls");
+    IPC_ASSERT(!AwaitingSyncReply(), "urgent calls cannot be issued within sync sends");
+
+    AutoEnterPendingReply replies(mPendingUrgentReplies);
+    if (!SendAndWait(aMsg, aReply))
+        return false;
+
+    NS_ABORT_IF_FALSE(aReply->is_urgent(), "reply is not urgent");
+    return true;
 }
 
 bool
-RPCChannel::Send(Message* msg, Message* reply)
+MessageChannel::SendAndWait(Message* aMsg, Message* aReply)
 {
-    Message copy = *msg;
-    CxxStackFrame f(*this, OUT_MESSAGE, &copy);
-    return SyncChannel::Send(msg, reply);
+    mMonitor->AssertCurrentThreadOwns();
+
+    nsAutoPtr<Message> msg(aMsg);
+
+    if (!Connected()) {
+        ReportConnectionError("MessageChannel::SendAndWait");
+        return false;
+    }
+
+    msg->set_seqno(NextSeqno());
+
+    DebugOnly<int32_t> replySeqno = msg->seqno();
+    DebugOnly<msgid_t> replyType = msg->type() + 1;
+
+    mLink->SendMessage(msg.forget());
+
+    while (true) {
+        // Wait for an event to occur.
+        while (true) {
+            if (mRecvd || mPendingUrgentRequest)
+                break;
+
+            bool maybeTimedOut = !WaitForSyncNotify();
+
+            if (!Connected()) {
+                ReportConnectionError("MessageChannel::SendAndWait");
+                return false;
+            }
+
+            if (maybeTimedOut && !ShouldContinueFromTimeout())
+                return false;
+        }
+
+        if (mPendingUrgentRequest) {
+            // Note that it is possible we could have sent a sync message at
+            // the same time the parent process sent an urgent message, and
+            // therefore mPendingUrgentRequest is set *and* mRecvd is set as
+            // well. In this case we always process the urgent request first.
+            // However, if mRecvd is not set, we assert that it does not
+            // become set by DispatchMessage(), since the parent should be
+            // blocked.
+            bool hadSyncReply = !!mRecvd;
+
+            nsAutoPtr<Message> recvd(mPendingUrgentRequest.forget());
+            {
+                MonitorAutoUnlock unlock(*mMonitor);
+                DispatchMessage(*recvd);
+            }
+            if (!Connected()) {
+                ReportConnectionError("MessageChannel::DispatchMessage");
+                return false;
+            }
+
+            IPC_ASSERT(!hadSyncReply || !mRecvd, "incoherent mRecvd state");
+        }
+
+        if (mRecvd) {
+            NS_ABORT_IF_FALSE(mRecvd->is_reply(), "expected reply");
+
+            if (mRecvd->is_reply_error()) {
+                mRecvd = nullptr;
+                return false;
+            }
+
+            NS_ABORT_IF_FALSE(mRecvd->type() == replyType, "wrong reply type");
+            NS_ABORT_IF_FALSE(mRecvd->seqno() == replySeqno, "wrong sequence number");
+
+            *aReply = *mRecvd;
+            mRecvd = nullptr;
+            return true;
+        }
+    }
+
+    return true;
 }
 
 bool
-RPCChannel::Call(Message* _msg, Message* reply)
+MessageChannel::Call(Message* aMsg, Message* aReply)
 {
-    RPC_ASSERT(!mPendingReply, "should not be waiting for a reply");
+    if (aMsg->is_urgent())
+        return UrgentCall(aMsg, aReply);
+    return RPCCall(aMsg, aReply);
+}
 
-    nsAutoPtr<Message> msg(_msg);
+bool
+MessageChannel::RPCCall(Message* aMsg, Message* aReply)
+{
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
-    RPC_ASSERT(!ProcessingSyncMessage() || msg->priority() == IPC::Message::PRIORITY_HIGH,
-               "violation of sync handler invariant");
-    RPC_ASSERT(msg->is_rpc(), "can only Call() RPC messages here");
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, true);
 #endif
 
-    Message copy = *msg;
-    CxxStackFrame f(*this, OUT_MESSAGE, &copy);
+    // This must come before MonitorAutoLock, as its destructor acquires the
+    // monitor lock.
+    Message copy = *aMsg;
+    CxxStackFrame cxxframe(*this, OUT_MESSAGE, &copy);
 
     MonitorAutoLock lock(*mMonitor);
-
     if (!Connected()) {
-        ReportConnectionError("RPCChannel");
+        ReportConnectionError("MessageChannel::Call");
         return false;
     }
 
-    bool urgent = (copy.priority() == IPC::Message::PRIORITY_HIGH);
+    // Sanity checks.
+    IPC_ASSERT(!AwaitingSyncReply() && !AwaitingUrgentReply(),
+               "cannot issue RPC call whiel blocked on sync or urgent");
+    IPC_ASSERT(!DispatchingSyncMessage() || aMsg->priority() == IPC::Message::PRIORITY_HIGH,
+               "violation of sync handler invariant");
+    IPC_ASSERT(aMsg->is_rpc(), "can only Call() RPC messages here");
+
+
+    nsAutoPtr<Message> msg(aMsg);
 
     msg->set_seqno(NextSeqno());
     msg->set_rpc_remote_stack_depth_guess(mRemoteStackDepthGuess);
-    msg->set_rpc_local_stack_depth(1 + StackDepth());
-    mStack.push(*msg);
-
+    msg->set_rpc_local_stack_depth(1 + RPCStackDepth());
+    mRPCStack.push(*msg);
     mLink->SendMessage(msg.forget());
 
-    while (1) {
+    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("RPCChannel");
+            ReportConnectionError("MessageChannel::RPCCall");
             return false;
         }
 
-        // now might be the time to process a message deferred because
-        // of race resolution
+        // Now might be the time to process a message deferred because of race
+        // resolution.
         MaybeUndeferIncall();
 
-        // here we're waiting for something to happen. see long
-        // comment about the queue in RPCChannel.h
-        while (!EventOccurred()) {
-            bool maybeTimedOut = !RPCChannel::WaitForNotify();
+        // Wait for an event to occur.
+        while (!RPCEventOccurred()) {
+            bool maybeTimedOut = !WaitForRPCNotify();
 
-            if (EventOccurred() ||
-                // we might have received a "subtly deferred" message
-                // in a nested loop that it's now time to process
-                (!maybeTimedOut &&
-                 (!mDeferred.empty() || !mOutOfTurnReplies.empty())))
+            // We might have received a "subtly deferred" message in a nested
+            // loop that it's now time to process.
+            if (RPCEventOccurred() ||
+                (!maybeTimedOut && (!mDeferred.empty() || !mOutOfTurnReplies.empty())))
+            {
                 break;
+            }
 
             if (maybeTimedOut && !ShouldContinueFromTimeout())
                 return false;
         }
 
-        if (!Connected()) {
-            ReportConnectionError("RPCChannel");
-            return false;
-        }
-
         Message recvd;
         MessageMap::iterator it;
-        if (!mOutOfTurnReplies.empty() &&
-            ((it = mOutOfTurnReplies.find(mStack.top().seqno())) !=
-            mOutOfTurnReplies.end())) {
+
+        if (mPendingUrgentRequest) {
+            recvd = *mPendingUrgentRequest;
+            mPendingUrgentRequest = nullptr;
+        } else if ((it = mOutOfTurnReplies.find(mRPCStack.top().seqno()))
+                    != mOutOfTurnReplies.end())
+        {
             recvd = it->second;
             mOutOfTurnReplies.erase(it);
-        }
-        else if (!mUrgent.empty()) {
-            recvd = mUrgent.front();
-            mUrgent.pop_front();
-        }
-        else if (!mPending.empty()) {
+        } else if (!mPending.empty()) {
             recvd = mPending.front();
             mPending.pop_front();
-        }
-        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.
+        } 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 RPC, we can dispatch it as normal.
         if (!recvd.is_rpc()) {
-            if (urgent && recvd.priority() != IPC::Message::PRIORITY_HIGH) {
-                // If we're waiting for an urgent reply, don't process any
-                // messages yet.
-                mNonUrgentDeferred.push_back(recvd);
-            } else if (recvd.is_sync()) {
-                RPC_ASSERT(mPending.empty(),
-                           "other side should have been blocked");
+            // Other side should be blocked.
+            IPC_ASSERT(!recvd.is_sync() || mPending.empty(), "other side should be blocked");
+
+            {
                 MonitorAutoUnlock unlock(*mMonitor);
-                CxxStackFrame f(*this, IN_MESSAGE, &recvd);
-                SyncChannel::OnDispatchMessage(recvd);
-            } else {
-                MonitorAutoUnlock unlock(*mMonitor);
-                CxxStackFrame f(*this, IN_MESSAGE, &recvd);
-                AsyncChannel::OnDispatchMessage(recvd);
+                CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
+                DispatchMessage(recvd);
+            }
+            if (!Connected()) {
+                ReportConnectionError("MessageChannel::DispatchMessage");
+                return false;
             }
             continue;
         }
 
-        RPC_ASSERT(recvd.is_rpc(), "wtf???");
-
+        // If the message is an RPC 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()) {
-            RPC_ASSERT(0 < mStack.size(), "invalid RPC stack");
+            IPC_ASSERT(!mRPCStack.empty(), "invalid RPC stack");
 
-            const Message& outcall = mStack.top();
+            // 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 Message &outcall = mRPCStack.top();
 
-            // in the parent, seqno's increase from 0, and in the
-            // child, they decrease from 0
-            if ((!mChild && recvd.seqno() < outcall.seqno()) ||
-                (mChild && recvd.seqno() > outcall.seqno())) {
-                mOutOfTurnReplies[recvd.seqno()] = recvd;
-                continue;
+                // 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()] = recvd;
+                    continue;
+                }
+
+                IPC_ASSERT(recvd.is_reply_error() ||
+                           (recvd.type() == (outcall.type() + 1) &&
+                            recvd.seqno() == outcall.seqno()),
+                           "somebody's misbehavin'", true);
             }
 
-            // FIXME/cjones: handle error
-            RPC_ASSERT(
-                recvd.is_reply_error() ||
-                (recvd.type() == (outcall.type()+1) &&
-                 recvd.seqno() == outcall.seqno()),
-                "somebody's misbehavin'", "rpc", true);
+            // We received a reply to our most recent outstanding call. Pop
+            // this frame and return the reply.
+            mRPCStack.pop();
 
-            // we received a reply to our most recent outstanding
-            // call.  pop this frame and return the reply
-            mStack.pop();
-
-            bool isError = recvd.is_reply_error();
-            if (!isError) {
-                *reply = recvd;
+            if (!recvd.is_reply_error()) {
+                *aReply = recvd;
             }
 
-            if (0 == StackDepth()) {
-                RPC_ASSERT(
-                    mOutOfTurnReplies.empty(),
-                    "still have pending replies with no pending out-calls",
-                    "rpc", true);
-            }
+            // If we have no more pending out calls waiting on replies, then
+            // the reply queue should be empty.
+            IPC_ASSERT(!mRPCStack.empty() || mOutOfTurnReplies.empty(),
+                       "still have pending replies with no pending out-calls",
+                       true);
 
-            // finished with this RPC stack frame
-            return !isError;
+            return !recvd.is_reply_error();
         }
 
-        // in-call.  process in a new stack frame.
-
-        // "snapshot" the current stack depth while we own the Monitor
-        size_t stackDepth = StackDepth();
+        // Dispatch an RPC in-call. Snapshot the current stack depth while we
+        // own the monitor.
+        size_t stackDepth = RPCStackDepth();
         {
             MonitorAutoUnlock unlock(*mMonitor);
-            // someone called in to us from the other side.  handle the call
-            CxxStackFrame f(*this, IN_MESSAGE, &recvd);
-            Incall(recvd, stackDepth);
-            // FIXME/cjones: error handling
+
+            CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
+            DispatchRPCMessage(recvd, stackDepth);
+        }
+        if (!Connected()) {
+            ReportConnectionError("MessageChannel::DispatchRPCMessage");
+            return false;
         }
     }
 
     return true;
 }
 
+bool
+MessageChannel::RPCEventOccurred()
+{
+    AssertWorkerThread();
+    mMonitor->AssertCurrentThreadOwns();
+    IPC_ASSERT(RPCStackDepth() > 0, "not in wait loop");
+
+    return (!Connected() ||
+            !mPending.empty() ||
+            mPendingUrgentRequest ||
+            (!mOutOfTurnReplies.empty() &&
+             mOutOfTurnReplies.find(mRPCStack.top().seqno()) !=
+             mOutOfTurnReplies.end()));
+}
+
+bool
+MessageChannel::OnMaybeDequeueOne()
+{
+    AssertWorkerThread();
+    mMonitor->AssertNotCurrentThreadOwns();
+
+    Message recvd;
+    do {
+        MonitorAutoLock lock(*mMonitor);
+
+        if (!Connected()) {
+            ReportConnectionError("OnMaybeDequeueOne");
+            return false;
+        }
+
+        if (mPendingUrgentRequest) {
+            recvd = *mPendingUrgentRequest;
+            mPendingUrgentRequest = nullptr;
+            break;
+        }
+
+        if (!mDeferred.empty())
+            MaybeUndeferIncall();
+
+        if (mPending.empty())
+            return false;
+
+        recvd = mPending.front();
+        mPending.pop_front();
+    } while (0);
+
+    if (IsOnCxxStack() && recvd.is_rpc() && recvd.is_reply()) {
+        // We probably just received a reply in a nested loop for an
+        // RPC call sent before entering that loop.
+        mOutOfTurnReplies[recvd.seqno()] = recvd;
+        return false;
+    }
+
+    CxxStackFrame frame(*this, IN_MESSAGE, &recvd);
+    DispatchMessage(recvd);
+    return true;
+}
+
 void
-RPCChannel::MaybeUndeferIncall()
+MessageChannel::DispatchMessage(const Message &aMsg)
+{
+    if (aMsg.is_sync())
+        DispatchSyncMessage(aMsg);
+    else if (aMsg.is_urgent())
+        DispatchUrgentMessage(aMsg);
+    else if (aMsg.is_rpc())
+        DispatchRPCMessage(aMsg, 0);
+    else
+        DispatchAsyncMessage(aMsg);
+}
+
+void
+MessageChannel::DispatchSyncMessage(const Message& aMsg)
+{
+    AssertWorkerThread();
+
+    Message *reply = nullptr;
+
+    mDispatchingSyncMessage = true;
+    Result rv = mListener->OnMessageReceived(aMsg, reply);
+    mDispatchingSyncMessage = false;
+
+    if (!MaybeHandleError(rv, "DispatchSyncMessage")) {
+        delete reply;
+        reply = new Message();
+        reply->set_sync();
+        reply->set_reply();
+        reply->set_reply_error();
+    }
+    reply->set_seqno(aMsg.seqno());
+
+    MonitorAutoLock lock(*mMonitor);
+    if (ChannelConnected == mChannelState)
+        mLink->SendMessage(reply);
+}
+
+void
+MessageChannel::DispatchUrgentMessage(const Message& aMsg)
+{
+    AssertWorkerThread();
+    MOZ_ASSERT(aMsg.is_urgent());
+
+    Message *reply = nullptr;
+
+    if (!MaybeHandleError(mListener->OnCallReceived(aMsg, reply), "DispatchUrgentMessage")) {
+        delete reply;
+        reply = new Message();
+        reply->set_urgent();
+        reply->set_reply();
+        reply->set_reply_error();
+    }
+    reply->set_seqno(aMsg.seqno());
+
+    MonitorAutoLock lock(*mMonitor);
+    if (ChannelConnected == mChannelState)
+        mLink->SendMessage(reply);
+}
+
+void
+MessageChannel::DispatchAsyncMessage(const Message& aMsg)
+{
+    AssertWorkerThread();
+    MOZ_ASSERT(!aMsg.is_rpc() && !aMsg.is_sync() && !aMsg.is_urgent());
+
+    if (aMsg.routing_id() == MSG_ROUTING_NONE) {
+        NS_RUNTIMEABORT("unhandled special message!");
+    }
+
+    MaybeHandleError(mListener->OnMessageReceived(aMsg), "DispatchAsyncMessage");
+}
+
+void
+MessageChannel::DispatchRPCMessage(const Message& aMsg, size_t stackDepth)
+{
+    AssertWorkerThread();
+    mMonitor->AssertNotCurrentThreadOwns();
+
+    IPC_ASSERT(aMsg.is_rpc() && !aMsg.is_reply(), "wrong message type");
+
+    // Race detection: see the long comment near mRemoteStackDepthGuess in
+    // RPCChannel.h. "Remote" stack depth means our side, and "local" means
+    // the other side.
+    if (aMsg.rpc_remote_stack_depth_guess() != RemoteViewOfStackDepth(stackDepth)) {
+        // RPC in-calls have raced. The winner, if there is one, gets to defer
+        // processing of the other side's in-call.
+        bool defer;
+        const char* winner;
+        switch (mListener->MediateRPCRace((mSide == ChildSide) ? aMsg : mRPCStack.top(),
+                                          (mSide != ChildSide) ? mRPCStack.top() : aMsg))
+        {
+          case RRPChildWins:
+            winner = "child";
+            defer = (mSide == ChildSide);
+            break;
+          case RRPParentWins:
+            winner = "parent";
+            defer = (mSide != ChildSide);
+            break;
+          case RRPError:
+            NS_RUNTIMEABORT("NYI: 'Error' RPC race policy");
+            return;
+          default:
+            NS_RUNTIMEABORT("not reached");
+            return;
+        }
+
+        if (LoggingEnabled()) {
+            printf_stderr("  (%s: %s won, so we're%sdeferring)\n",
+                          (mSide == ChildSide) ? "child" : "parent",
+                          winner,
+                          defer ? " " : " not ");
+        }
+
+        if (defer) {
+            // We now know the other side's stack has one more frame
+            // than we thought.
+            ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
+            mDeferred.push(aMsg);
+            return;
+        }
+
+        // We "lost" and need to process the other side's in-call. Don't need
+        // to fix up the mRemoteStackDepthGuess here, because we're just about
+        // to increment it in DispatchCall(), which will make it correct again.
+    }
+
+#ifdef OS_WIN
+    SyncStackFrame frame(this, true);
+#endif
+
+    Message* reply = nullptr;
+
+    ++mRemoteStackDepthGuess;
+    Result rv = mListener->OnCallReceived(aMsg, reply);
+    --mRemoteStackDepthGuess;
+
+    if (!MaybeHandleError(rv, "DispatchRPCMessage")) {
+        delete reply;
+        reply = new Message();
+        reply->set_rpc();
+        reply->set_reply();
+        reply->set_reply_error();
+    }
+    reply->set_seqno(aMsg.seqno());
+
+    MonitorAutoLock lock(*mMonitor);
+    if (ChannelConnected == mChannelState)
+        mLink->SendMessage(reply);
+}
+
+void
+MessageChannel::MaybeUndeferIncall()
 {
     AssertWorkerThread();
     mMonitor->AssertCurrentThreadOwns();
 
     if (mDeferred.empty())
         return;
 
-    size_t stackDepth = StackDepth();
+    size_t stackDepth = RPCStackDepth();
 
     // the other side can only *under*-estimate our actual stack depth
-    RPC_ASSERT(mDeferred.top().rpc_remote_stack_depth_guess() <= stackDepth,
+    IPC_ASSERT(mDeferred.top().rpc_remote_stack_depth_guess() <= stackDepth,
                "fatal logic error");
 
     if (mDeferred.top().rpc_remote_stack_depth_guess() < RemoteViewOfStackDepth(stackDepth))
         return;
 
     // maybe time to process this message
     Message call = mDeferred.top();
     mDeferred.pop();
 
     // fix up fudge factor we added to account for race
-    RPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
+    IPC_ASSERT(0 < mRemoteStackDepthGuess, "fatal logic error");
     --mRemoteStackDepthGuess;
 
     mPending.push_back(call);
 }
 
 void
-RPCChannel::EnqueuePendingMessages()
-{
-    AssertWorkerThread();
-    mMonitor->AssertCurrentThreadOwns();
-
-    MaybeUndeferIncall();
-
-    for (size_t i = 0; i < mDeferred.size(); ++i) {
-        mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
-    }
-
-    // XXX performance tuning knob: could process all or k pending
-    // messages here, rather than enqueuing for later processing
-
-    size_t total = mPending.size() + mUrgent.size() + mNonUrgentDeferred.size();
-    for (size_t i = 0; i < total; ++i) {
-        mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
-    }
-}
-
-void
-RPCChannel::FlushPendingRPCQueue()
+MessageChannel::FlushPendingRPCQueue()
 {
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
     {
         MonitorAutoLock lock(*mMonitor);
 
         if (mDeferred.empty()) {
@@ -333,190 +912,432 @@ RPCChannel::FlushPendingRPCQueue()
             if (!last.is_rpc() || last.is_reply())
                 return;
         }
     }
 
     while (OnMaybeDequeueOne());
 }
 
-bool
-RPCChannel::OnMaybeDequeueOne()
-{
-    // XXX performance tuning knob: could process all or k pending
-    // messages here
-
-    AssertWorkerThread();
-    mMonitor->AssertNotCurrentThreadOwns();
-
-    Message recvd;
-    {
-        MonitorAutoLock lock(*mMonitor);
-
-        if (!Connected()) {
-            ReportConnectionError("RPCChannel");
-            return false;
-        }
-
-        if (!mDeferred.empty())
-            MaybeUndeferIncall();
-
-        MessageQueue *queue = mUrgent.empty()
-                              ? mNonUrgentDeferred.empty()
-                                ? &mPending
-                                : &mNonUrgentDeferred
-                              : &mUrgent;
-        if (queue->empty())
-            return false;
-
-        recvd = queue->front();
-        queue->pop_front();
-    }
-
-    if (IsOnCxxStack() && recvd.is_rpc() && recvd.is_reply()) {
-        // We probably just received a reply in a nested loop for an
-        // RPC call sent before entering that loop.
-        mOutOfTurnReplies[recvd.seqno()] = recvd;
-        return false;
-    }
-
-    CxxStackFrame f(*this, IN_MESSAGE, &recvd);
-
-    if (recvd.is_rpc())
-        Incall(recvd, 0);
-    else if (recvd.is_sync())
-        SyncChannel::OnDispatchMessage(recvd);
-    else
-        AsyncChannel::OnDispatchMessage(recvd);
-
-    return true;
-}
-
-size_t
-RPCChannel::RemoteViewOfStackDepth(size_t stackDepth) const
-{
-    AssertWorkerThread();
-    return stackDepth - mOutOfTurnReplies.size();
-}
-
 void
-RPCChannel::Incall(const Message& call, size_t stackDepth)
+MessageChannel::ExitedCxxStack()
 {
-    AssertWorkerThread();
-    mMonitor->AssertNotCurrentThreadOwns();
-    RPC_ASSERT(call.is_rpc() && !call.is_reply(), "wrong message type");
-
-    // Race detection: see the long comment near
-    // mRemoteStackDepthGuess in RPCChannel.h.  "Remote" stack depth
-    // means our side, and "local" means other side.
-    if (call.rpc_remote_stack_depth_guess() != RemoteViewOfStackDepth(stackDepth)) {
-        // RPC in-calls have raced.
-        // the "winner", if there is one, gets to defer processing of
-        // the other side's in-call
-        bool defer;
-        const char* winner;
-        switch (Listener()->MediateRPCRace(mChild ? call : mStack.top(),
-                                           mChild ? mStack.top() : call)) {
-        case RRPChildWins:
-            winner = "child";
-            defer = mChild;
-            break;
-        case RRPParentWins:
-            winner = "parent";
-            defer = !mChild;
-            break;
-        case RRPError:
-            NS_RUNTIMEABORT("NYI: 'Error' RPC race policy");
-            return;
-        default:
-            NS_RUNTIMEABORT("not reached");
-            return;
-        }
-
-        if (LoggingEnabled()) {
-            printf_stderr("  (%s: %s won, so we're%sdeferring)\n",
-                          mChild ? "child" : "parent", winner,
-                          defer ? " " : " not ");
-        }
-
-        if (defer) {
-            // we now know the other side's stack has one more frame
-            // than we thought
-            ++mRemoteStackDepthGuess; // decremented in MaybeProcessDeferred()
-            mDeferred.push(call);
-            return;
-        }
-
-        // we "lost" and need to process the other side's in-call.
-        // don't need to fix up the mRemoteStackDepthGuess here,
-        // because we're just about to increment it in DispatchCall(),
-        // which will make it correct again
-    }
-
-#ifdef OS_WIN
-    SyncStackFrame frame(this, true);
-#endif
-
-    DispatchIncall(call);
-}
-
-void
-RPCChannel::DispatchIncall(const Message& call)
-{
-    AssertWorkerThread();
-    mMonitor->AssertNotCurrentThreadOwns();
-    RPC_ASSERT(call.is_rpc() && !call.is_reply(),
-               "wrong message type");
-
-    Message* reply = nullptr;
-
-    ++mRemoteStackDepthGuess;
-    Result rv = Listener()->OnCallReceived(call, reply);
-    --mRemoteStackDepthGuess;
-
-    if (!MaybeHandleError(rv, "RPCChannel")) {
-        delete reply;
-        reply = new Message();
-        reply->set_rpc();
-        reply->set_reply();
-        reply->set_reply_error();
-    }
-
-    reply->set_seqno(call.seqno());
-
-    {
-        MonitorAutoLock lock(*mMonitor);
-        if (ChannelConnected == mChannelState)
-            mLink->SendMessage(reply);
-    }
-}
-
-void
-RPCChannel::ExitedCxxStack()
-{
-    Listener()->OnExitedCxxStack();
+    mListener->OnExitedCxxStack();
     if (mSawRPCOutMsg) {
         MonitorAutoLock lock(*mMonitor);
         // see long comment in OnMaybeDequeueOne()
         EnqueuePendingMessages();
         mSawRPCOutMsg = false;
     }
 }
 
 void
-RPCChannel::DebugAbort(const char* file, int line, const char* cond,
-                       const char* why,
-                       const char* type, bool reply) const
+MessageChannel::EnqueuePendingMessages()
+{
+    AssertWorkerThread();
+    mMonitor->AssertCurrentThreadOwns();
+
+    MaybeUndeferIncall();
+
+    for (size_t i = 0; i < mDeferred.size(); ++i) {
+        mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
+    }
+
+    // XXX performance tuning knob: could process all or k pending
+    // messages here, rather than enqueuing for later processing
+
+    for (size_t i = 0; i < mPending.size(); ++i) {
+        mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
+    }
+}
+
+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()
+{
+    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::WaitForRPCNotify()
+{
+    return WaitForSyncNotify();
+}
+
+void
+MessageChannel::NotifyWorkerThread()
+{
+    mMonitor->Notify();
+}
+#endif
+
+bool
+MessageChannel::ShouldContinueFromTimeout()
+{
+    AssertWorkerThread();
+    mMonitor->AssertCurrentThreadOwns();
+
+    bool cont;
+    {
+        MonitorAutoUnlock unlock(*mMonitor);
+        cont = mListener->OnReplyTimeout();
+    }
+
+    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;
+    }
+
+    if (!cont) {
+        // NB: there's a sublety here.  If parents were allowed to send sync
+        // messages to children, then it would be possible for this
+        // synchronous close-on-timeout to race with async |OnMessageReceived|
+        // tasks arriving from the child, posted to the worker thread's event
+        // loop.  This would complicate cleanup of the *Channel.  But since
+        // IPDL forbids this (and since it doesn't support children timing out
+        // on parents), the parent can only block on RPC messages to the child,
+        // and in that case arriving async messages are enqueued to the RPC
+        // channel's special queue.  They're then ignored because the channel
+        // state changes to ChannelTimeout (i.e. !Connected).
+        SynchronouslyClose();
+        mChannelState = ChannelTimeout;
+    }
+
+    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)
+{
+    mWorkerLoop->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this,
+                          &MessageChannel::DispatchOnChannelConnected,
+                          peer_id));
+}
+
+void
+MessageChannel::DispatchOnChannelConnected(int32_t peer_pid)
+{
+    AssertWorkerThread();
+    if (mListener)
+        mListener->OnChannelConnected(peer_pid);
+}
+
+
+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);
+}
+
+void
+MessageChannel::ReportConnectionError(const char* aChannelName) const
+{
+    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");
+    }
+
+    PrintErrorMessage(mSide, aChannelName, errorMsg);
+    mListener->OnProcessingError(MsgDropped);
+}
+
+bool
+MessageChannel::MaybeHandleError(Result code, 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;
+    }
+
+    PrintErrorMessage(mSide, channelName, errorMsg);
+
+    mListener->OnProcessingError(code);
+
+    return false;
+}
+
+void
+MessageChannel::OnChannelErrorFromLink()
+{
+    AssertLinkThread();
+    mMonitor->AssertCurrentThreadOwns();
+
+    if (RPCStackDepth() > 0)
+        NotifyWorkerThread();
+
+    if (AwaitingSyncReply())
+        NotifyWorkerThread();
+
+    if (ChannelClosing != mChannelState) {
+        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;
+    }
+
+    // Oops, error!  Let the listener know about it.
+    mChannelState = ChannelError;
+    mListener->OnChannelError();
+    Clear();
+}
+
+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 =
+            NewRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError);
+        // 10 ms delay is completely arbitrary
+        mWorkerLoop->PostDelayedTask(FROM_HERE, mChannelErrorTask, 10);
+        return;
+    }
+
+    NotifyMaybeChannelError();
+}
+
+void
+MessageChannel::PostErrorNotifyTask()
+{
+    mMonitor->AssertCurrentThreadOwns();
+
+    if (mChannelErrorTask)
+        return;
+
+    // This must be the last code that runs on this thread!
+    mChannelErrorTask =
+        NewRunnableMethod(this, &MessageChannel::OnNotifyMaybeChannelError);
+    mWorkerLoop->PostTask(FROM_HERE, mChannelErrorTask);
+}
+
+// Special async message.
+class GoodbyeMessage : public IPC::Message
+{
+public:
+    GoodbyeMessage() :
+        IPC::Message(MSG_ROUTING_NONE, GOODBYE_MESSAGE_TYPE, PRIORITY_NORMAL)
+    {
+    }
+    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::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 (ChannelConnected != mChannelState) {
+            // XXX be strict about this until there's a compelling reason
+            // to relax
+            NS_RUNTIMEABORT("Close() called on closed channel!");
+        }
+
+        AssertWorkerThread();
+
+        // notify the other side that we're about to close our socket
+        mLink->SendMessage(new GoodbyeMessage());
+        SynchronouslyClose();
+    }
+
+    NotifyChannelClosed();
+}
+
+void
+MessageChannel::NotifyChannelClosed()
+{
+    mMonitor->AssertNotCurrentThreadOwns();
+
+    if (ChannelClosed != mChannelState)
+        NS_RUNTIMEABORT("channel should have been closed!");
+
+    // OK, the IO thread just closed the channel normally.  Let the
+    // listener know about it.
+    mListener->OnChannelClose();
+
+    Clear();
+}
+
+void
+MessageChannel::DebugAbort(const char* file, int line, const char* cond,
+                           const char* why,
+                           bool reply) const
 {
     printf_stderr("###!!! [RPCChannel][%s][%s:%d] "
-                  "Assertion (%s) failed.  %s (triggered by %s%s)\n",
-                  mChild ? "Child" : "Parent",
+                  "Assertion (%s) failed.  %s %s\n",
+                  mSide == ChildSide ? "Child" : "Parent",
                   file, line, cond,
                   why,
-                  type, reply ? "reply" : "");
+                  reply ? "(reply)" : "");
     // technically we need the mutex for this, but we're dying anyway
     DumpRPCStack("  ");
     printf_stderr("  remote RPC stack guess: %lu\n",
                   mRemoteStackDepthGuess);
     printf_stderr("  deferred stack size: %lu\n",
                   mDeferred.size());
     printf_stderr("  out-of-turn RPC replies stack size: %lu\n",
                   mOutOfTurnReplies.size());
@@ -531,17 +1352,17 @@ RPCChannel::DebugAbort(const char* file,
                       pending.front().is_reply() ? "reply" : "");
         pending.pop_front();
     }
 
     NS_RUNTIMEABORT(why);
 }
 
 void
-RPCChannel::DumpRPCStack(const char* const pfx) const
+MessageChannel::DumpRPCStack(const char* const pfx) const
 {
     NS_WARN_IF_FALSE(MessageLoop::current() != mWorkerLoop,
                      "The worker thread had better be paused in a debugger!");
 
     printf_stderr("%sRPCChannel 'backtrace':\n", pfx);
 
     // print a python-style backtrace, first frame to last
     for (uint32_t i = 0; i < mCxxStackFrames.size(); ++i) {
@@ -549,97 +1370,11 @@ RPCChannel::DumpRPCStack(const char* con
         const char* dir, *sems, *name;
         mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
 
         printf_stderr("%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
                       i, dir, sems, name, id);
     }
 }
 
-//
-// The methods below run in the context of the link thread, and can proxy
-// back to the methods above
-//
-
-void
-RPCChannel::OnMessageReceivedFromLink(const Message& msg)
-{
-    AssertLinkThread();
-    mMonitor->AssertCurrentThreadOwns();
-
-    if (MaybeInterceptSpecialIOMessage(msg))
-        return;
-
-    // regardless of the RPC stack, if we're awaiting a sync reply, we
-    // know that it needs to be immediately handled to unblock us.
-    if (AwaitingSyncReply() && msg.is_sync()) {
-        // wake up worker thread waiting at SyncChannel::Send
-        mRecvd = msg;
-        NotifyWorkerThread();
-        return;
-    }
-
-    MessageQueue *queue = (msg.priority() == IPC::Message::PRIORITY_HIGH)
-                          ? &mUrgent
-                          : &mPending;
-
-    bool compressMessage = (msg.compress() && !queue->empty() &&
-                            queue->back().type() == msg.type() &&
-                            queue->back().routing_id() == msg.routing_id());
-    if (compressMessage) {
-        // 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_ASSERT(queue->back().compress());
-        queue->pop_back();
-    }
-
-    queue->push_back(msg);
+} // ipc
+} // mozilla
 
-    // 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 high priority, we wake up the main thread to
-    //     deliver the message. 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 RPC reply - main thread is blocked on the IPC monitor.
-    //   - Always wake up the main thread to deliver the message.
-    //
-    // (3) We are not waiting on a reply.
-    //   - We post a task to the main event loop.
-    //
-    bool waiting_rpc = (0 != StackDepth());
-    bool urgent = (msg.priority() == IPC::Message::PRIORITY_HIGH);
-
-    if (waiting_rpc || (AwaitingSyncReply() && urgent)) {
-        // Always wake up our RPC waiter, and wake up sync waiters for urgent
-        // messages.
-        NotifyWorkerThread();
-    } else {
-        // Worker thread is either not blocked on a reply, or this is an
-        // incoming RPC that raced with outgoing sync and needs to be deferred
-        // to a later event-loop iteration.
-        if (!compressMessage) {
-            // If we compressed away the previous message, we'll reuse
-            // its pending task.
-            mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
-        }
-    }
-}
-
-void
-RPCChannel::OnChannelErrorFromLink()
-{
-    AssertLinkThread();
-    mMonitor->AssertCurrentThreadOwns();
-
-    if (0 < StackDepth())
-        NotifyWorkerThread();
-
-    SyncChannel::OnChannelErrorFromLink();
-}
-
-} // namespace ipc
-} // namespace mozilla
-
rename from ipc/glue/RPCChannel.h
rename to ipc/glue/MessageChannel.h
--- a/ipc/glue/RPCChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -1,203 +1,287 @@
 /* -*- 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
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.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_RPCChannel_h
-#define ipc_glue_RPCChannel_h 1
+#ifndef ipc_glue_MessageChannel_h
+#define ipc_glue_MessageChannel_h 1
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
 
-#include <stdio.h>
+#include "mozilla/WeakPtr.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/ipc/Transport.h"
+#include "MessageLink.h"
+#include "nsAutoPtr.h"
+#include "mozilla/DebugOnly.h"
 
 #include <deque>
 #include <stack>
 #include <vector>
-
-#include "base/basictypes.h"
-
-#include "nsISupportsImpl.h"
-
-#include "mozilla/ipc/SyncChannel.h"
-#include "nsAutoPtr.h"
+#include <math.h>
 
 namespace mozilla {
 namespace ipc {
-//-----------------------------------------------------------------------------
 
-class RPCChannel : public SyncChannel
-{
-    friend class CxxStackFrame;
+class MessageChannel;
 
-public:
-    // What happens if RPC calls race?
-    enum RacyRPCPolicy {
-        RRPError,
-        RRPChildWins,
-        RRPParentWins
-    };
+class RefCountedMonitor : public Monitor
+{
+  public:
+    RefCountedMonitor()
+        : Monitor("mozilla.ipc.MessageChannel.mMonitor")
+    {}
+
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedMonitor)
+};
 
-    class /*NS_INTERFACE_CLASS*/ RPCListener :
-        public SyncChannel::SyncListener
-    {
-    public:
-        virtual ~RPCListener() { }
+class MessageChannel : HasResultCodes
+{
+    friend class ProcessLink;
+    friend class ThreadLink;
+
+    typedef mozilla::Monitor Monitor;
 
-        virtual void OnChannelClose() = 0;
-        virtual void OnChannelError() = 0;
-        virtual Result OnMessageReceived(const Message& aMessage) = 0;
-        virtual void OnProcessingError(Result aError) = 0;
-        virtual int32_t GetProtocolTypeId() = 0;
-        virtual bool OnReplyTimeout() = 0;
-        virtual Result OnMessageReceived(const Message& aMessage,
-                                         Message*& aReply) = 0;
-        virtual Result OnCallReceived(const Message& aMessage,
-                                      Message*& aReply) = 0;
-        virtual void OnChannelConnected(int32_t peer_pid) {}
+  public:
+    static const int32_t kNoTimeout;
+
+    typedef IPC::Message Message;
+    typedef mozilla::ipc::Transport Transport;
+
+    MessageChannel(MessageListener *aListener);
+    ~MessageChannel();
 
-        virtual void OnEnteredCxxStack()
-        {
-            NS_RUNTIMEABORT("default impl shouldn't be invoked");
-        }
+    // "Open" from the perspective of the transport layer; the underlying
+    // socketpair/pipe should already be created.
+    //
+    // Returns true iff the transport layer was successfully connected,
+    // i.e., mChannelState == ChannelConnected.
+    bool Open(Transport* aTransport, MessageLoop* aIOLoop=0, Side aSide=UnknownSide);
 
-        virtual void OnExitedCxxStack()
-        {
-            NS_RUNTIMEABORT("default impl shouldn't be invoked");
-        }
-
-        virtual void OnEnteredCall()
-        {
-            NS_RUNTIMEABORT("default impl shouldn't be invoked");
-        }
+    // "Open" a connection to another thread in the same process.
+    //
+    // Returns true iff 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);
 
-        virtual void OnExitedCall()
-        {
-            NS_RUNTIMEABORT("default impl shouldn't be invoked");
-        }
+    // 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();
 
-        virtual RacyRPCPolicy MediateRPCRace(const Message& parent,
-                                             const Message& child)
-        {
-            return RRPChildWins;
-        }
-        virtual void ProcessRemoteNativeEventsInRPCCall() {};
-    };
+    // Asynchronously send a message to the other side of the channel
+    bool Send(Message* aMsg);
 
-    RPCChannel(RPCListener* aListener);
+    // Asynchronously deliver a message back to this side of the
+    // channel
+    bool Echo(Message* aMsg);
 
-    virtual ~RPCChannel();
-
-    void Clear() MOZ_OVERRIDE;
+    // Synchronously send |msg| (i.e., wait for |reply|)
+    bool Send(Message* aMsg, Message* aReply);
 
     // Make an RPC to the other side of the channel
-    bool Call(Message* msg, Message* reply);
+    bool Call(Message* aMsg, Message* aReply);
 
-    // RPCChannel overrides these so that the async and sync messages
-    // can be counted against mStackFrames
-    virtual bool Send(Message* msg) MOZ_OVERRIDE;
-    virtual bool Send(Message* msg, Message* reply) MOZ_OVERRIDE;
+    void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
-    // Return true iff this has code on the C++ stack.
     bool IsOnCxxStack() const {
         return !mCxxStackFrames.empty();
     }
 
-    /**
-     * If there is a pending RPC message, process all pending messages.
-     *
-     * @note This method is used on Windows when we detect that an outbound
-     * OLE RPC call is being made to unblock the parent.
-     */
     void FlushPendingRPCQueue();
 
+    // 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 rpc);
+        ~SyncStackFrame();
+
+        bool mRPC;
+        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;
+
+    // The deepest sync stack frame on any channel.
+    static SyncStackFrame* sStaticTopFrame;
+
+  public:
     void ProcessNativeEventsInRPCCall();
     static void NotifyGeckoEventDispatch();
 
-protected:
-    bool WaitForNotify();
+  private:
     void SpinInternalEventLoop();
 #endif
 
-protected:
-    virtual void OnMessageReceivedFromLink(const Message& msg) MOZ_OVERRIDE;
-    virtual void OnChannelErrorFromLink() MOZ_OVERRIDE;
+  private:
+    void CommonThreadOpenInit(MessageChannel *aTargetChan, Side aSide);
+    void OnOpenAsSlave(MessageChannel *aTargetChan, Side aSide);
 
-private:
-    // Called on worker thread only
+    void PostErrorNotifyTask();
+    void OnNotifyMaybeChannelError();
+    void ReportConnectionError(const char* aChannelName) const;
+    bool MaybeHandleError(Result code, const char* channelName);
+
+    void Clear();
+
+    // Send OnChannelConnected notification to listeners.
+    void DispatchOnChannelConnected(int32_t peer_pid);
 
-    RPCListener* Listener() const {
-        return static_cast<RPCListener*>(mListener.get());
-    }
+    // Any protocol that requires blocking until a reply arrives, will send its
+    // outgoing message through this function. Currently, two protocols do this:
+    //
+    //  sync, which can only initiate messages from child to parent.
+    //  urgent, which can only initiate messages from parent to child.
+    //
+    // SendAndWait() expects that the worker thread owns the monitor, and that
+    // the message has been prepared to be sent over the link. It returns as
+    // soon as a reply has been received, or an error has occurred.
+    //
+    // Note that while the child is blocked waiting for a sync reply, it can wake
+    // up to process urgent calls from the parent.
+    bool SendAndWait(Message* aMsg, Message* aReply);
 
-    virtual bool ShouldDeferNotifyMaybeError() const MOZ_OVERRIDE {
-        return IsOnCxxStack();
-    }
+    bool RPCCall(Message* aMsg, Message* aReply);
+    bool UrgentCall(Message* aMsg, Message* aReply);
 
-    bool EventOccurred() const;
+    bool RPCEventOccurred();
 
     void MaybeUndeferIncall();
     void EnqueuePendingMessages();
 
-    /**
-     * Process one deferred or pending message.
-     * @return true if a message was processed
-     */
+    // Executed on the worker thread. Dequeues one pending message.
     bool OnMaybeDequeueOne();
 
-    /**
-     * 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 RPC 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;
+    // Dispatches an incoming message to its appropriate handler.
+    void DispatchMessage(const Message &aMsg);
+
+    // DispatchMessage will route to one of these functions depending on the
+    // protocol type of the message.
+    void DispatchSyncMessage(const Message &aMsg);
+    void DispatchUrgentMessage(const Message &aMsg);
+    void DispatchAsyncMessage(const Message &aMsg);
+    void DispatchRPCMessage(const 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 WaitForRPCNotify();
+
+    bool WaitResponse(bool aWaitTimedOut);
 
-    void Incall(const Message& call, size_t stackDepth);
-    void DispatchIncall(const Message& call);
+    bool ShouldContinueFromTimeout();
 
-    // This helper class managed RPCChannel.mCxxStackDepth on behalf
-    // of RPCChannel.  When the stack depth is incremented from zero
-    // to non-zero, it invokes an RPCChannel callback, and similarly
-    // for when the depth goes from non-zero to zero;
-    void EnteredCxxStack()
-    {
-        Listener()->OnEnteredCxxStack();
+    // 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 RPC 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
+    // an RPCChannel callback, and similarly for when the depth goes from
+    // non-zero to zero.
+    void EnteredCxxStack() {
+       mListener->OnEnteredCxxStack();
     }
 
     void ExitedCxxStack();
 
-    void EnteredCall()
-    {
-        Listener()->OnEnteredCall();
+    void EnteredCall() {
+        mListener->OnEnteredCall();
     }
 
-    void ExitedCall()
-    {
-        Listener()->OnExitedCall();
+    void ExitedCall() {
+        mListener->OnExitedCall();
+    }
+
+    MessageListener *Listener() const {
+        return mListener.get();
     }
 
     enum Direction { IN_MESSAGE, OUT_MESSAGE };
     struct RPCFrame {
-        RPCFrame(Direction direction, const Message* msg) :
-            mDirection(direction), mMsg(msg)
+        RPCFrame(Direction direction, const Message* msg)
+          : mDirection(direction), mMsg(msg)
         { }
 
-        bool IsRPCIncall() const
-        {
+        bool IsRPCIncall() const {
             return mMsg->is_rpc() && IN_MESSAGE == mDirection;
         }
-
-        bool IsRPCOutcall() const
-        {
+        bool IsRPCOutcall() const {
             return mMsg->is_rpc() && OUT_MESSAGE == mDirection;
         }
 
         void Describe(int32_t* id, const char** dir, const char** sems,
                       const char** name) const
         {
             *id = mMsg->routing_id();
             *dir = (IN_MESSAGE == mDirection) ? "in" : "out";
@@ -206,20 +290,20 @@ private:
         }
 
         Direction mDirection;
         const Message* mMsg;
     };
 
     class MOZ_STACK_CLASS CxxStackFrame
     {
-    public:
-
-        CxxStackFrame(RPCChannel& that, Direction direction,
-                      const Message* msg) : mThat(that) {
+      public:
+        CxxStackFrame(MessageChannel& that, Direction direction, const Message* msg)
+          : mThat(that)
+        {
             mThat.AssertWorkerThread();
 
             if (mThat.mCxxStackFrames.empty())
                 mThat.EnteredCxxStack();
 
             mThat.mCxxStackFrames.push_back(RPCFrame(direction, msg));
             const RPCFrame& frame = mThat.mCxxStackFrames.back();
 
@@ -230,198 +314,299 @@ private:
         }
 
         ~CxxStackFrame() {
             bool exitingCall = mThat.mCxxStackFrames.back().IsRPCIncall();
             mThat.mCxxStackFrames.pop_back();
             bool exitingStack = mThat.mCxxStackFrames.empty();
 
             // mListener could have gone away if Close() was called while
-            // RPCChannel code was still on the stack
+            // MessageChannel code was still on the stack
             if (!mThat.mListener)
                 return;
 
             mThat.AssertWorkerThread();
             if (exitingCall)
                 mThat.ExitedCall();
 
             if (exitingStack)
                 mThat.ExitedCxxStack();
         }
-    private:
-        RPCChannel& mThat;
+      private:
+        MessageChannel& mThat;
 
         // disable harmful methods
         CxxStackFrame();
         CxxStackFrame(const CxxStackFrame&);
         CxxStackFrame& operator=(const CxxStackFrame&);
     };
 
-    // Called from both threads
-    size_t StackDepth() const {
-        mMonitor->AssertCurrentThreadOwns();
-        return mStack.size();
-    }
-
     void DebugAbort(const char* file, int line, const char* cond,
                     const char* why,
-                    const char* type="rpc", bool reply=false) const;
+                    bool reply=false) const;
 
     // This method is only safe to call on the worker thread, or in a
     // debugger with all threads paused.
     void DumpRPCStack(const char* const pfx="") const;
 
-    // 
-    // Queue of all incoming messages, except for replies to sync
-    // messages, which are delivered directly to the SyncChannel
-    // through its mRecvd member.
-    //
-    // 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 RPC in-call,
-    //   |R<| be an RPC reply.
-    // 
-    // The queue can only match this configuration
-    // 
-    //  A<* (S< | C< | R< (?{mStack.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 RPC 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,
-    // |?{mStack.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 RPC call on our stack, the other side *better* not have
-    // sent us another blocking message, because it's blocked on a
-    // reply from us.
-    //
-    typedef std::deque<Message> MessageQueue;
-    MessageQueue mPending;
+  private:
+    // Called from both threads
+    size_t RPCStackDepth() const {
+        mMonitor->AssertCurrentThreadOwns();
+        return mRPCStack.size();
+    }
 
-    // List of async and sync messages that have been received while waiting
-    // for an urgent reply, that need to be deferred until that reply has been
-    // received.
-    MessageQueue mNonUrgentDeferred;
+    // Returns true if we're blocking waiting for a reply.
+    bool AwaitingSyncReply() const {
+        mMonitor->AssertCurrentThreadOwns();
+        return mPendingSyncReplies > 0;
+    }
+    bool AwaitingUrgentReply() const {
+        mMonitor->AssertCurrentThreadOwns();
+        return mPendingUrgentReplies > 0;
+    }
+    bool AwaitingRPCReply() const {
+        mMonitor->AssertCurrentThreadOwns();
+        return !mRPCStack.empty();
+    }
 
-    // 
-    // Stack of all the RPC out-calls on which this RPCChannel is
-    // awaiting a response.
-    //
-    std::stack<Message> mStack;
+    // Returns true if we're dispatching a sync message's callback.
+    bool DispatchingSyncMessage() const {
+        return mDispatchingSyncMessage;
+    }
+
+    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);
 
-    //
-    // Map of replies received "out of turn", because of RPC
-    // in-calls racing with replies to outstanding in-calls.  See
-    // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
-    //
-    typedef std::map<size_t, Message> MessageMap;
-    MessageMap mOutOfTurnReplies;
+    void OnChannelConnected(int32_t peer_id);
+
+    // Tell the IO thread to close the channel and wait for it to ACK.
+    void SynchronouslyClose();
 
-    //
-    // Stack of RPC in-calls that were deferred because of race
-    // conditions.
-    //
-    std::stack<Message> mDeferred;
+    void OnMessageReceivedFromLink(const Message& aMsg);
+    void OnChannelErrorFromLink();
+
+  private:
+    // Run on the not current thread.
+    void NotifyChannelClosed();
+    void NotifyMaybeChannelError();
 
-    //
-    // This is what we think the RPC stack depth is on the "other
-    // side" of this RPC channel.  We maintain this variable so that
-    // we can detect racy RPC calls.  With each RPC out-call sent, we
-    // send along what *we* think the stack depth of the remote side
-    // is *before* it will receive the RPC 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
-    //
-    //   mStack.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 RPC 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;
+  private:
+    // Can be run on either thread
+    void AssertWorkerThread() const
+    {
+        NS_ABORT_IF_FALSE(mWorkerLoopID == MessageLoop::current()->id(),
+                          "not on worker thread!");
+    }
 
-    // Approximation of Sync/RPCChannel-code frames on the C++ stack.
-    // It can only be interpreted as the implication
-    //
-    //  !mCxxStackFrames.empty() => RPCChannel 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|.
-    std::vector<RPCFrame> mCxxStackFrames;
+    // 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
+    {
+        NS_ABORT_IF_FALSE(mWorkerLoopID != MessageLoop::current()->id(),
+                          "on worker thread but should not be!");
+    }
 
-    // Did we process an RPC out-call during this stack?  Only
-    // meaningful in ExitedCxxStack(), from which this variable is
-    // reset.
-    bool mSawRPCOutMsg;
+  private:
+    typedef IPC::Message::msgid_t msgid_t;
+    typedef std::deque<Message> MessageQueue;
+    typedef std::map<size_t, Message> MessageMap;
 
-private:
-
-    //
     // All dequeuing tasks require a single point of cancellation,
     // which is handled via a reference-counted task.
-    //
     class RefCountedTask
     {
       public:
         RefCountedTask(CancelableTask* aTask)
-        : mTask(aTask) {}
+          : mTask(aTask)
+        { }
         ~RefCountedTask() { delete mTask; }
         void Run() { mTask->Run(); }
         void Cancel() { mTask->Cancel(); }
 
         NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefCountedTask)
 
       private:
         CancelableTask* mTask;
     };
 
-    //
     // Wrap an existing task which can be cancelled at any time
     // without the wrapper's knowledge.
-    //
     class DequeueTask : public Task
     {
       public:
-        DequeueTask(RefCountedTask* aTask) : mTask(aTask) {}
+        DequeueTask(RefCountedTask* aTask)
+          : mTask(aTask)
+        { }
         void Run() { mTask->Run(); }
-        
+
       private:
         nsRefPtr<RefCountedTask> mTask;
     };
 
-    // A task encapsulating dequeuing one pending task
+  private:
+    mozilla::WeakPtr<MessageListener> mListener;
+    ChannelState mChannelState;
+    nsRefPtr<RefCountedMonitor> mMonitor;
+    Side mSide;
+    MessageLink* mLink;
+    MessageLoop* mWorkerLoop;           // thread where work is done
+    CancelableTask* mChannelErrorTask;  // NotifyMaybeChannelError runnable
+
+    // id() of mWorkerLoop.  This persists even after mWorkerLoop is cleared
+    // during channel shutdown.
+    int mWorkerLoopID;
+
+    // A task encapsulating dequeuing one pending message.
     nsRefPtr<RefCountedTask> mDequeueOneTask;
+
+    // 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;
+
+    class AutoEnterPendingReply {
+      public:
+        AutoEnterPendingReply(size_t &replyVar)
+          : mReplyVar(replyVar)
+        {
+            mReplyVar++;
+        }
+        ~AutoEnterPendingReply() {
+            mReplyVar--;
+        }
+      private:
+        size_t& mReplyVar;
+    };
+
+    // Worker-thread only; type we're expecting for the reply to a sync
+    // out-message. This will never be greater than 1.
+    size_t mPendingSyncReplies;
+
+    // Worker-thread only; type we're expecting for the reply to an
+    // urgent out-message. This will never be greater than 1.
+    size_t mPendingUrgentReplies;
+
+    // If waiting for the reply to a sync out-message, it will be saved here
+    // on the I/O thread and then read and cleared by the worker thread.
+    nsAutoPtr<Message> mRecvd;
+
+    // Set while we are dispatching a synchronous message.
+    bool mDispatchingSyncMessage;
+
+    // Queue of all incoming messages, except for replies to sync and urgent
+    // messages, which are delivered directly to mRecvd, and any pending urgent
+    // incall, which is stored in mPendingUrgentRequest.
+    //
+    // 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 RPC in-call,
+    //   |R<| be an RPC reply.
+    //
+    // The queue can only match this configuration
+    //
+    //  A<* (S< | C< | R< (?{mStack.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 RPC 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, |?{mStack.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 RPC 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;
+    nsAutoPtr<Message> mPendingUrgentRequest;
+
+    // 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<Message> mRPCStack;
+
+    // This is what we think the RPC stack depth is on the "other side" of this
+    // RPC channel.  We maintain this variable so that we can detect racy RPC
+    // calls.  With each RPC out-call sent, we send along what *we* think the
+    // stack depth of the remote side is *before* it will receive the RPC 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
+    //
+    //   mStack.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 RPC 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|.
+    std::vector<RPCFrame> mCxxStackFrames;
+
+    // Did we process an RPC out-call during this stack?  Only meaningful in
+    // ExitedCxxStack(), from which this variable is reset.
+    bool mSawRPCOutMsg;
+
+    // Map of replies received "out of turn", because of RPC
+    // in-calls racing with replies to outstanding in-calls.  See
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=521929.
+    MessageMap mOutOfTurnReplies;
+
+    // Stack of RPC in-calls that were deferred because of race
+    // conditions.
+    std::stack<Message> mDeferred;
+
+#ifdef OS_WIN
+    HANDLE mEvent;
+#endif
 };
 
-
 } // namespace ipc
 } // namespace mozilla
-#endif  // ifndef ipc_glue_RPCChannel_h
+
+#endif  // ifndef ipc_glue_MessageChannel_h
new file mode 100644
--- /dev/null
+++ b/ipc/glue/MessageLink.cpp
@@ -0,0 +1,367 @@
+/* -*- 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 "nsDebug.h"
+#include "nsTraceRefcnt.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla;
+using namespace std;
+
+template<>
+struct RunnableMethodTraits<mozilla::ipc::ProcessLink>
+{
+    static void RetainCallee(mozilla::ipc::ProcessLink* obj) { }
+    static void ReleaseCallee(mozilla::ipc::ProcessLink* obj) { }
+};
+
+// 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.
+template<>
+struct RunnableMethodTraits<mozilla::ipc::MessageChannel::Transport>
+{
+    static void RetainCallee(mozilla::ipc::MessageChannel::Transport* obj) { }
+    static void ReleaseCallee(mozilla::ipc::MessageChannel::Transport* obj) { }
+};
+
+namespace mozilla {
+namespace ipc {
+
+MessageLink::MessageLink(MessageChannel *aChan)
+  : mChan(aChan)
+{
+}
+
+MessageLink::~MessageLink()
+{
+    mChan = nullptr;
+}
+
+ProcessLink::ProcessLink(MessageChannel *aChan)
+  : MessageLink(aChan),
+    mExistingListener(NULL)
+{
+}
+
+ProcessLink::~ProcessLink()
+{
+    mIOLoop = 0;
+    if (mTransport) {
+        mTransport->set_listener(0);
+        
+        // we only hold a weak ref to the transport, which is "owned"
+        // by GeckoChildProcess/GeckoThread
+        mTransport = 0;
+    }
+}
+
+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(
+                FROM_HERE,
+                NewRunnableMethod(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(
+                FROM_HERE,
+                NewRunnableMethod(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(
+        FROM_HERE,
+        NewRunnableMethod(this, &ProcessLink::OnEchoMessage, msg));
+    // OnEchoMessage takes ownership of |msg|
+}
+
+void
+ProcessLink::SendMessage(Message *msg)
+{
+    mChan->AssertWorkerThread();
+    mChan->mMonitor->AssertCurrentThreadOwns();
+
+    mIOLoop->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(mTransport, &Transport::Send, msg));
+}
+
+void
+ProcessLink::SendClose()
+{
+    mChan->AssertWorkerThread();
+    mChan->mMonitor->AssertCurrentThreadOwns();
+
+    mIOLoop->PostTask(
+        FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnCloseChannel));
+}
+
+ThreadLink::ThreadLink(MessageChannel *aChan, MessageChannel *aTargetChan)
+  : MessageLink(aChan),
+    mTargetChan(aTargetChan)
+{
+}
+
+ThreadLink::~ThreadLink()
+{
+    // :TODO: 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.
+    if (mTargetChan) {
+        static_cast<ThreadLink*>(mTargetChan->mLink)->mTargetChan = 0;
+    }
+    mTargetChan = 0;
+}
+
+void
+ThreadLink::EchoMessage(Message *msg)
+{
+    mChan->AssertWorkerThread();
+    mChan->mMonitor->AssertCurrentThreadOwns();
+
+    mChan->OnMessageReceivedFromLink(*msg);
+    delete msg;
+}
+
+void
+ThreadLink::SendMessage(Message *msg)
+{
+    mChan->AssertWorkerThread();
+    mChan->mMonitor->AssertCurrentThreadOwns();
+
+    if (mTargetChan)
+        mTargetChan->OnMessageReceivedFromLink(*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(const Message& msg)
+{
+    AssertIOThread();
+    NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
+    MonitorAutoLock lock(*mChan->mMonitor);
+    mChan->OnMessageReceivedFromLink(msg);
+}
+
+void
+ProcessLink::OnEchoMessage(Message* msg)
+{
+    AssertIOThread();
+    OnMessageReceived(*msg);
+    delete msg;
+}
+
+void
+ProcessLink::OnChannelOpened()
+{
+    mChan->AssertLinkThread();
+    {
+        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(pending.front());
+        pending.pop();
+    }
+}
+
+void
+ProcessLink::OnChannelConnected(int32_t peer_pid)
+{
+    AssertIOThread();
+
+    {
+        MonitorAutoLock lock(*mChan->mMonitor);
+        mChan->mChannelState = ChannelConnected;
+        mChan->mMonitor->Notify();
+    }
+
+    if (mExistingListener)
+        mExistingListener->OnChannelConnected(peer_pid);
+
+    mChan->OnChannelConnected(peer_pid);
+}
+
+void
+ProcessLink::OnChannelError()
+{
+    AssertIOThread();
+    MonitorAutoLock lock(*mChan->mMonitor);
+    mChan->OnChannelErrorFromLink();
+}
+
+void
+ProcessLink::OnCloseChannel()
+{
+    AssertIOThread();
+
+    mTransport->Close();
+
+    MonitorAutoLock lock(*mChan->mMonitor);
+    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
new file mode 100644
--- /dev/null
+++ b/ipc/glue/MessageLink.h
@@ -0,0 +1,187 @@
+/* -*- 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
+};
+
+enum ChannelState {
+    ChannelClosed,
+    ChannelOpening,
+    ChannelConnected,
+    ChannelTimeout,
+    ChannelClosing,
+    ChannelError
+};
+
+// What happens if RPC calls race?
+enum RacyRPCPolicy {
+    RRPError,
+    RRPChildWins,
+    RRPParentWins
+};
+
+class MessageListener
+  : protected HasResultCodes,
+    public mozilla::SupportsWeakPtr<MessageListener>
+{
+  public:
+    typedef IPC::Message Message;
+
+    virtual ~MessageListener() { }
+
+    virtual void OnChannelClose() = 0;
+    virtual void OnChannelError() = 0;
+    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 void OnProcessingError(Result aError) = 0;
+    virtual void OnChannelConnected(int32_t peer_pid) {}
+    virtual bool OnReplyTimeout() {
+        return false;
+    }
+
+    virtual void OnEnteredCxxStack() {
+        NS_RUNTIMEABORT("default impl shouldn't be invoked");
+    }
+    virtual void OnExitedCxxStack() {
+        NS_RUNTIMEABORT("default impl shouldn't be invoked");
+    }
+    virtual void OnEnteredCall() {
+        NS_RUNTIMEABORT("default impl shouldn't be invoked");
+    }
+    virtual void OnExitedCall() {
+        NS_RUNTIMEABORT("default impl shouldn't be invoked");
+    }
+    virtual RacyRPCPolicy MediateRPCRace(const Message& parent,
+                                         const Message& child)
+    {
+        return RRPChildWins;
+    }
+
+    virtual void ProcessRemoteNativeEventsInRPCCall() {
+    }
+
+    // FIXME/bug 792652: this doesn't really belong here, but a
+    // large refactoring is needed to put it where it belongs.
+    virtual int32_t GetProtocolTypeId() = 0;
+};
+
+class MessageLink
+{
+  public:
+    typedef IPC::Message Message;
+
+    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
+    {
+        NS_ABORT_IF_FALSE(mIOLoop == MessageLoop::current(),
+                          "not on I/O thread!");
+    }
+
+  public:
+    ProcessLink(MessageChannel *chan);
+    virtual ~ProcessLink();
+    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(const Message& msg) MOZ_OVERRIDE;
+    virtual void OnChannelConnected(int32_t peer_pid) MOZ_OVERRIDE;
+    virtual void OnChannelError() MOZ_OVERRIDE;
+
+    virtual void EchoMessage(Message *msg) MOZ_OVERRIDE;
+    virtual void SendMessage(Message *msg) MOZ_OVERRIDE;
+    virtual void SendClose() MOZ_OVERRIDE;
+
+    virtual bool Unsound_IsClosed() const MOZ_OVERRIDE;
+    virtual uint32_t Unsound_NumQueuedMessages() const MOZ_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) MOZ_OVERRIDE;
+    virtual void SendMessage(Message *msg) MOZ_OVERRIDE;
+    virtual void SendClose() MOZ_OVERRIDE;
+
+    virtual bool Unsound_IsClosed() const MOZ_OVERRIDE;
+    virtual uint32_t Unsound_NumQueuedMessages() const MOZ_OVERRIDE;
+
+  protected:
+    MessageChannel* mTargetChan;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif  // ifndef ipc_glue_MessageLink_h
+
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -2,17 +2,17 @@
  * 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 "base/process_util.h"
 
-#include "mozilla/ipc/AsyncChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/Transport.h"
 
 using namespace base;
 using namespace IPC;
 
 namespace mozilla {
 namespace ipc {
@@ -45,18 +45,18 @@ public:
     }
     aMsg.EndRead(iter);
     return true;
   }
 };
 
 bool
 Bridge(const PrivateIPDLInterface&,
-       AsyncChannel* aParentChannel, ProcessHandle aParentProcess,
-       AsyncChannel* aChildChannel, ProcessHandle aChildProcess,
+       MessageChannel* aParentChannel, ProcessHandle aParentProcess,
+       MessageChannel* aChildChannel, ProcessHandle aChildProcess,
        ProtocolId aProtocol, ProtocolId aChildProtocol)
 {
   ProcessId parentId = GetProcId(aParentProcess);
   ProcessId childId = GetProcId(aChildProcess);
   if (!parentId || !childId) {
     return false;
   }
 
@@ -76,17 +76,17 @@ Bridge(const PrivateIPDLInterface&,
     CloseDescriptor(childSide);
     return false;
   }
   return true;
 }
 
 bool
 Open(const PrivateIPDLInterface&,
-     AsyncChannel* aOpenerChannel, ProcessHandle aOtherProcess,
+     MessageChannel* aOpenerChannel, ProcessHandle aOtherProcess,
      Transport::Mode aOpenerMode,
      ProtocolId aProtocol, ProtocolId aChildProtocol)
 {
   bool isParent = (Transport::MODE_SERVER == aOpenerMode);
   ProcessHandle thisHandle = GetCurrentProcessHandle();
   ProcessHandle parentHandle = isParent ? thisHandle : aOtherProcess;
   ProcessHandle childHandle = !isParent ? thisHandle : aOtherProcess;
   ProcessId parentId = GetProcId(parentHandle);
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -37,17 +37,17 @@ enum {
 
     // kuint16max - 1 is used by ipc_channel.h.
 };
 }
 
 namespace mozilla {
 namespace ipc {
 
-class AsyncChannel;
+class MessageChannel;
 
 // 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;
 };
@@ -94,17 +94,17 @@ public:
         size_t, SharedMemory::SharedMemoryType, bool, int32_t*) = 0;
     virtual bool AdoptSharedMemory(Shmem::SharedMemory*, int32_t*) = 0;
     virtual Shmem::SharedMemory* LookupSharedMemory(int32_t) = 0;
     virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*) = 0;
     virtual bool DestroySharedMemory(Shmem&) = 0;
 
     // XXX odd ducks, acknowledged
     virtual ProcessHandle OtherProcess() const = 0;
-    virtual AsyncChannel* GetIPCChannel() = 0;
+    virtual MessageChannel* GetIPCChannel() = 0;
 };
 
 
 inline bool
 LoggingEnabled()
 {
 #if defined(DEBUG)
     return !!PR_GetEnv("MOZ_IPC_MESSAGE_LOG");
@@ -121,22 +121,22 @@ FatalError(const char* aProtocolName, co
            base::ProcessHandle aHandle, bool aIsParent);
 
 typedef IPCMessageStart ProtocolId;
 
 struct PrivateIPDLInterface {};
 
 bool
 Bridge(const PrivateIPDLInterface&,
-       AsyncChannel*, base::ProcessHandle, AsyncChannel*, base::ProcessHandle,
+       MessageChannel*, base::ProcessHandle, MessageChannel*, base::ProcessHandle,
        ProtocolId, ProtocolId);
 
 bool
 Open(const PrivateIPDLInterface&,
-     AsyncChannel*, base::ProcessHandle, Transport::Mode,
+     MessageChannel*, base::ProcessHandle, Transport::Mode,
      ProtocolId, ProtocolId);
 
 bool
 UnpackChannelOpened(const PrivateIPDLInterface&,
                     const IPC::Message&,
                     TransportDescriptor*, base::ProcessId*, ProtocolId*);
 
 } // namespace ipc
deleted file mode 100644
--- a/ipc/glue/SyncChannel.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/* -*- 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/DebugOnly.h"
-
-#include "mozilla/ipc/SyncChannel.h"
-#include "mozilla/ipc/RPCChannel.h"
-
-#include "nsDebug.h"
-#include "nsTraceRefcnt.h"
-
-using mozilla::MonitorAutoLock;
-
-template<>
-struct RunnableMethodTraits<mozilla::ipc::SyncChannel>
-{
-    static void RetainCallee(mozilla::ipc::SyncChannel* obj) { }
-    static void ReleaseCallee(mozilla::ipc::SyncChannel* obj) { }
-};
-
-namespace mozilla {
-namespace ipc {
-
-const int32_t SyncChannel::kNoTimeout = INT32_MIN;
-
-SyncChannel::SyncChannel(SyncListener* aListener)
-  : AsyncChannel(aListener)
-#ifdef OS_WIN
-  , mTopFrame(nullptr)
-#endif
-  , mPendingReply(0)
-  , mProcessingSyncMessage(false)
-  , mNextSeqno(0)
-  , mInTimeoutSecondHalf(false)
-  , mTimeoutMs(kNoTimeout)
-{
-    MOZ_COUNT_CTOR(SyncChannel);
-#ifdef OS_WIN
-    mEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
-    NS_ASSERTION(mEvent, "CreateEvent failed! Nothing is going to work!");
-#endif
-}
-
-SyncChannel::~SyncChannel()
-{
-    MOZ_COUNT_DTOR(SyncChannel);
-#ifdef OS_WIN
-    CloseHandle(mEvent);
-#endif
-}
-
-// static
-bool SyncChannel::sIsPumpingMessages = false;
-
-bool
-SyncChannel::EventOccurred()
-{
-    AssertWorkerThread();
-    mMonitor->AssertCurrentThreadOwns();
-    NS_ABORT_IF_FALSE(AwaitingSyncReply(), "not in wait loop");
-
-    return !Connected() ||
-           mRecvd.type() != 0 ||
-           mRecvd.is_reply_error() ||
-           !mUrgent.empty();
-}
-
-bool
-SyncChannel::ProcessUrgentMessages()
-{
-    while (!mUrgent.empty()) {
-        Message msg = mUrgent.front();
-        mUrgent.pop_front();
-
-        MOZ_ASSERT(msg.priority() == IPC::Message::PRIORITY_HIGH);
-
-        {
-            MOZ_ASSERT(msg.is_sync() || msg.is_rpc());
-
-            MonitorAutoUnlock unlock(*mMonitor);
-            SyncChannel::OnDispatchMessage(msg);
-        }
-
-        // Check state that could have been changed during dispatch.
-        if (!Connected()) {
-            ReportConnectionError("SyncChannel");
-            return false;
-        }
-
-        // We should not have received another synchronous reply,
-        // because we cannot send synchronous messages in this state.
-        MOZ_ASSERT(mRecvd.type() == 0);
-    }
-
-    return true;
-}
-
-bool
-SyncChannel::Send(Message* _msg, Message* reply)
-{
-    if (mPendingReply) {
-        // This is a temporary hack in place, for e10s CPOWs, until bug 901789
-        // and the new followup RPC protocol land. Eventually this will become
-        // an assert again. See bug 900062 for details.
-        NS_ERROR("Nested sync messages are not supported");
-        return false;
-    }
-
-    nsAutoPtr<Message> msg(_msg);
-
-    AssertWorkerThread();
-    mMonitor->AssertNotCurrentThreadOwns();
-    NS_ABORT_IF_FALSE(!ProcessingSyncMessage(),
-                      "violation of sync handler invariant");
-    NS_ABORT_IF_FALSE(msg->is_sync(), "can only Send() sync messages here");
-
-#ifdef OS_WIN
-    SyncStackFrame frame(this, false);
-#endif
-
-    msg->set_seqno(NextSeqno());
-
-    MonitorAutoLock lock(*mMonitor);
-
-    if (!Connected()) {
-        ReportConnectionError("SyncChannel");
-        return false;
-    }
-
-    mPendingReply = msg->type() + 1;
-    DebugOnly<int32_t> msgSeqno = msg->seqno();
-    mLink->SendMessage(msg.forget());
-
-    while (1) {
-        // 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("SyncChannel");
-            return false;
-        }
-
-        while (!EventOccurred()) {
-            bool maybeTimedOut = !SyncChannel::WaitForNotify();
-
-            if (EventOccurred())
-                break;
-
-            // we might have received a "subtly deferred" message
-            // in a nested loop that it's now time to process
-            if (!mUrgent.empty())
-                break;
-
-            if (maybeTimedOut && !ShouldContinueFromTimeout())
-                return false;
-        }
-
-        if (!Connected()) {
-            ReportConnectionError("SyncChannel");
-
-            return false;
-        }
-
-        // Process all urgent messages. We forbid nesting synchronous sends,
-        // so mPendingReply etc will still be valid.
-        if (!ProcessUrgentMessages())
-            return false;
-
-        if (mRecvd.is_reply_error() || mRecvd.type() != 0) {
-            // we just received a synchronous message from the other side.
-            // If it's not the reply we were awaiting, there's a serious
-            // error: either a mistimed/malformed message or a sync in-message
-            // that raced with our sync out-message.
-            // (NB: IPDL prevents the latter from occuring in actor code)
-            // FIXME/cjones: real error handling
-            NS_ABORT_IF_FALSE(mRecvd.is_sync() && mRecvd.is_reply() &&
-                              (mRecvd.is_reply_error() ||
-                               (mPendingReply == mRecvd.type() &&
-                                msgSeqno == mRecvd.seqno())),
-                              "unexpected sync message");
-
-            mPendingReply = 0;
-            if (mRecvd.is_reply_error())
-                return false;
-
-            *reply = TakeReply();
-
-            MOZ_ASSERT(mUrgent.empty());
-            return true;
-        }
-    }
-
-    return true;
-}
-
-void
-SyncChannel::OnDispatchMessage(const Message& msg)
-{
-    AssertWorkerThread();
-    NS_ABORT_IF_FALSE(msg.is_sync() || msg.is_rpc(), "only sync messages here");
-    NS_ABORT_IF_FALSE(!msg.is_reply(), "wasn't awaiting reply");
-
-    Message* reply = 0;
-
-    mProcessingSyncMessage = true;
-    Result rv;
-    if (msg.is_sync())
-        rv = static_cast<SyncListener*>(mListener.get())->OnMessageReceived(msg, reply);
-    else
-        rv = static_cast<RPCChannel::RPCListener*>(mListener.get())->OnCallReceived(msg, reply);
-    mProcessingSyncMessage = false;
-
-    if (!MaybeHandleError(rv, "SyncChannel")) {
-        // FIXME/cjones: error handling; OnError()?
-        delete reply;
-        reply = new Message();
-        if (msg.is_sync())
-            reply->set_sync();
-        else if (msg.is_rpc())
-            reply->set_rpc();
-        reply->set_reply();
-        reply->set_reply_error();
-    }
-
-    reply->set_seqno(msg.seqno());
-
-    {
-        MonitorAutoLock lock(*mMonitor);
-        if (ChannelConnected == mChannelState)
-            mLink->SendMessage(reply);
-    }
-}
-
-//
-// The methods below run in the context of the link thread, and can proxy
-// back to the methods above
-//
-
-void
-SyncChannel::OnMessageReceivedFromLink(const Message& msg)
-{
-    AssertLinkThread();
-    mMonitor->AssertCurrentThreadOwns();
-
-    if (MaybeInterceptSpecialIOMessage(msg))
-        return;
-
-    if (msg.priority() == IPC::Message::PRIORITY_HIGH) {
-        // If the message is high priority, we skip the worker entirely, and
-        // wake up the loop that's spinning for a reply.
-        if (!AwaitingSyncReply()) {
-            mWorkerLoop->PostTask(
-                FROM_HERE,
-                NewRunnableMethod(this, &SyncChannel::OnDispatchMessage, msg));
-        } else {
-            mUrgent.push_back(msg);
-            NotifyWorkerThread();
-        }
-        return;
-    }
-
-    if (!msg.is_sync()) {
-        AsyncChannel::OnMessageReceivedFromLink(msg);
-        return;
-    }
-
-    if (!AwaitingSyncReply()) {
-        // wake up the worker, there's work to do
-        mWorkerLoop->PostTask(
-            FROM_HERE,
-            NewRunnableMethod(this, &SyncChannel::OnDispatchMessage, msg));
-    }
-    else {
-        // let the worker know a new sync message has arrived
-        mRecvd = msg;
-        NotifyWorkerThread();
-    }
-}
-
-void
-SyncChannel::OnChannelErrorFromLink()
-{
-    AssertLinkThread();
-    mMonitor->AssertCurrentThreadOwns();
-
-    if (AwaitingSyncReply())
-        NotifyWorkerThread();
-
-    AsyncChannel::OnChannelErrorFromLink();
-}
-
-//
-// Synchronization between worker and IO threads
-//
-
-namespace {
-
-bool
-IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
-{
-    return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
-        (aTimeout <= (PR_IntervalNow() - aStart));
-}
-
-} // namespace <anon>
-
-bool
-SyncChannel::ShouldContinueFromTimeout()
-{
-    AssertWorkerThread();
-    mMonitor->AssertCurrentThreadOwns();
-
-    bool cont;
-    {
-        MonitorAutoUnlock unlock(*mMonitor);
-        cont = static_cast<SyncListener*>(mListener.get())->OnReplyTimeout();
-    }
-
-    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;
-    }
-
-    if (!cont) {
-        // NB: there's a sublety here.  If parents were allowed to
-        // send sync messages to children, then it would be possible
-        // for this synchronous close-on-timeout to race with async
-        // |OnMessageReceived| tasks arriving from the child, posted
-        // to the worker thread's event loop.  This would complicate
-        // cleanup of the *Channel.  But since IPDL forbids this (and
-        // since it doesn't support children timing out on parents),
-        // the parent can only block on RPC messages to the child, and
-        // in that case arriving async messages are enqueued to the
-        // RPC channel's special queue.  They're then ignored because
-        // the channel state changes to ChannelTimeout
-        // (i.e. !Connected).
-        SynchronouslyClose();
-        mChannelState = ChannelTimeout;
-    }
-        
-    return cont;
-}
-
-bool
-SyncChannel::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;
-}
-
-
-// Windows versions of the following two functions live in
-// WindowsMessageLoop.cpp.
-
-#ifndef OS_WIN
-
-bool
-SyncChannel::WaitForNotify()
-{
-    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));
-}
-
-void
-SyncChannel::NotifyWorkerThread()
-{
-    mMonitor->Notify();
-}
-
-#endif  // ifndef OS_WIN
-
-
-} // namespace ipc
-} // namespace mozilla
deleted file mode 100644
--- a/ipc/glue/SyncChannel.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/* -*- 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_SyncChannel_h
-#define ipc_glue_SyncChannel_h 1
-
-#include "mozilla/ipc/AsyncChannel.h"
-
-#include <math.h>
-
-namespace mozilla {
-namespace ipc {
-//-----------------------------------------------------------------------------
-
-class SyncChannel : public AsyncChannel
-{
-protected:
-    typedef IPC::Message::msgid_t msgid_t;
-
-public:
-    static const int32_t kNoTimeout;
-
-    class /*NS_INTERFACE_CLASS*/ SyncListener : 
-        public AsyncChannel::AsyncListener
-    {
-    public:
-        virtual ~SyncListener() { }
-
-        virtual void OnChannelClose() = 0;
-        virtual void OnChannelError() = 0;
-        virtual Result OnMessageReceived(const Message& aMessage) = 0;
-        virtual void OnProcessingError(Result aError) = 0;
-        virtual int32_t GetProtocolTypeId() = 0;
-        virtual bool OnReplyTimeout() = 0;
-        virtual Result OnMessageReceived(const Message& aMessage,
-                                         Message*& aReply) = 0;
-        virtual void OnChannelConnected(int32_t peer_pid) {}
-    };
-
-    SyncChannel(SyncListener* aListener);
-    virtual ~SyncChannel();
-
-    virtual bool Send(Message* msg) MOZ_OVERRIDE {
-        return AsyncChannel::Send(msg);
-    }
-
-    // Synchronously send |msg| (i.e., wait for |reply|)
-    virtual bool Send(Message* msg, Message* reply);
-
-    // Set channel timeout value. Since this is broken up into
-    // two period, the minimum timeout value is 2ms.
-    void SetReplyTimeoutMs(int32_t aTimeoutMs) {
-        AssertWorkerThread();
-        mTimeoutMs = (aTimeoutMs <= 0) ? kNoTimeout :
-          // timeouts are broken up into two periods
-          (int32_t)ceil((double)aTimeoutMs/2.0);
-    }
-
-    static bool IsPumpingMessages() {
-        return sIsPumpingMessages;
-    }
-    static void SetIsPumpingMessages(bool aIsPumping) {
-        sIsPumpingMessages = aIsPumping;
-    }
-
-#ifdef OS_WIN
-public:
-    struct MOZ_STACK_CLASS SyncStackFrame
-    {
-        SyncStackFrame(SyncChannel* channel, bool rpc);
-        ~SyncStackFrame();
-
-        bool mRPC;
-        bool mSpinNestedEvents;
-        bool mListenerNotified;
-        SyncChannel* mChannel;
-
-        /* the previous stack frame for this channel */
-        SyncStackFrame* mPrev;
-
-        /* the previous stack frame on any channel */
-        SyncStackFrame* mStaticPrev;
-    };
-    friend struct SyncChannel::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;
-
-    /* the deepest sync stack frame on any channel */
-    static SyncStackFrame* sStaticTopFrame;
-#endif // OS_WIN
-
-protected:
-    // Executed on the link thread
-    // Override the AsyncChannel handler so we can dispatch sync messages
-    virtual void OnMessageReceivedFromLink(const Message& msg) MOZ_OVERRIDE;
-    virtual void OnChannelErrorFromLink() MOZ_OVERRIDE;
-
-    // Executed on the worker thread
-    bool ProcessingSyncMessage() const {
-        return mProcessingSyncMessage;
-    }
-
-    void OnDispatchMessage(const Message& aMsg);
-
-    //
-    // Return true if the wait ended because a notification was
-    // received.  That is, true => event 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 WaitForNotify();
-
-    bool ShouldContinueFromTimeout();
-
-    // Executed on the IO thread.
-    void NotifyWorkerThread();
-
-    // On both
-    bool AwaitingSyncReply() const {
-        mMonitor->AssertCurrentThreadOwns();
-        return mPendingReply != 0;
-    }
-
-    Message TakeReply() {
-        Message reply = mRecvd;
-        mRecvd = Message();
-        return reply;
-    }
-
-    int32_t NextSeqno() {
-        AssertWorkerThread();
-        return mChild ? --mNextSeqno : ++mNextSeqno;
-    }
-
-    msgid_t mPendingReply;
-    bool mProcessingSyncMessage;
-    Message mRecvd;
-    // This is only accessed from the worker thread; seqno's are
-    // completely opaque to the IO thread.
-    int32_t mNextSeqno;
-
-    static bool sIsPumpingMessages;
-
-    // Timeout periods are broken up in two to prevent system suspension from
-    // triggering an abort. This method (called by WaitForNotify with a 'did
-    // timeout' flag) decides if we should wait again for half of mTimeoutMs
-    // or give up.
-    bool WaitResponse(bool aWaitTimedOut);
-    bool mInTimeoutSecondHalf;
-    int32_t mTimeoutMs;
-
-    std::deque<Message> mUrgent;
-
-#ifdef OS_WIN
-    HANDLE mEvent;
-#endif
-
-private:
-    bool EventOccurred();
-    bool ProcessUrgentMessages();
-};
-
-
-} // namespace ipc
-} // namespace mozilla
-#endif  // ifndef ipc_glue_SyncChannel_h
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -3,17 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 
 #include "WindowsMessageLoop.h"
-#include "RPCChannel.h"
+#include "MessageChannel.h"
 
 #include "nsAutoPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsIXULAppInfo.h"
 
 #include "mozilla/PaintTracker.h"
 
@@ -110,20 +110,20 @@ DeferredMessageHook(int nCode,
 {
   // 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 WaitForNotify
-  //      function (indicated with SyncChannel::IsPumpingMessages). We really
+  //   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 && !SyncChannel::IsPumpingMessages()) {
+  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;
@@ -582,17 +582,17 @@ TimeoutHasExpired(const TimeoutData& aDa
     // Overflow
     return now < aData.startTicks && now >= aData.targetTicks;
   }
   return now >= aData.targetTicks;
 }
 
 } // anonymous namespace
 
-RPCChannel::SyncStackFrame::SyncStackFrame(SyncChannel* channel, bool rpc)
+MessageChannel::SyncStackFrame::SyncStackFrame(MessageChannel* channel, bool rpc)
   : mRPC(rpc)
   , mSpinNestedEvents(false)
   , mListenerNotified(false)
   , mChannel(channel)
   , mPrev(mChannel->mTopFrame)
   , mStaticPrev(sStaticTopFrame)
 {
   mChannel->mTopFrame = this;
@@ -600,72 +600,72 @@ RPCChannel::SyncStackFrame::SyncStackFra
 
   if (!mStaticPrev) {
     NS_ASSERTION(!gNeuteredWindows, "Should only set this once!");
     gNeuteredWindows = new nsAutoTArray<HWND, 20>();
     NS_ASSERTION(gNeuteredWindows, "Out of memory!");
   }
 }
 
-RPCChannel::SyncStackFrame::~SyncStackFrame()
+MessageChannel::SyncStackFrame::~SyncStackFrame()
 {
   NS_ASSERTION(this == mChannel->mTopFrame,
                "Mismatched RPC stack frames");
   NS_ASSERTION(this == sStaticTopFrame,
                "Mismatched static RPC stack frames");
 
   mChannel->mTopFrame = mPrev;
   sStaticTopFrame = mStaticPrev;
 
   if (!mStaticPrev) {
     NS_ASSERTION(gNeuteredWindows, "Bad pointer!");
     delete gNeuteredWindows;
     gNeuteredWindows = nullptr;
   }
 }
 
-SyncChannel::SyncStackFrame* SyncChannel::sStaticTopFrame;
+MessageChannel::SyncStackFrame* MessageChannel::sStaticTopFrame;
 
 // nsAppShell's notification that gecko events are being processed.
 // If we are here and there is an RPC Incall active, we are spinning
 // a nested gecko event loop. In which case the remote process needs
 // to know about it.
 void /* static */
-RPCChannel::NotifyGeckoEventDispatch()
+MessageChannel::NotifyGeckoEventDispatch()
 {
   // sStaticTopFrame is only valid for RPC channels
   if (!sStaticTopFrame || sStaticTopFrame->mListenerNotified)
     return;
 
   sStaticTopFrame->mListenerNotified = true;
-  RPCChannel* channel = static_cast<RPCChannel*>(sStaticTopFrame->mChannel);
+  MessageChannel* channel = static_cast<MessageChannel*>(sStaticTopFrame->mChannel);
   channel->Listener()->ProcessRemoteNativeEventsInRPCCall();
 }
 
 // invoked by the module that receives the spin event loop
 // message.
 void
-RPCChannel::ProcessNativeEventsInRPCCall()
+MessageChannel::ProcessNativeEventsInRPCCall()
 {
   if (!mTopFrame) {
     NS_ERROR("Spin logic error: no RPC frame");
     return;
   }
 
   mTopFrame->mSpinNestedEvents = true;
 }
 
-// Spin loop is called in place of WaitForNotify when modal ui is being shown
+// 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 RPC frame by the client calling
-// RPCChannel::ProcessNativeEventsInRPCCall().
+// MessageChannel::ProcessNativeEventsInRPCCall().
 // This call can be nested for multiple RPC frames in a single plugin or 
 // multiple unrelated plugins.
 void
-RPCChannel::SpinInternalEventLoop()
+MessageChannel::SpinInternalEventLoop()
 {
   if (mozilla::PaintTracker::IsPainting()) {
     NS_RUNTIMEABORT("Don't spin an event loop while painting.");
   }
 
   NS_ASSERTION(mTopFrame && mTopFrame->mSpinNestedEvents,
                "Spinning incorrectly");
 
@@ -689,17 +689,17 @@ RPCChannel::SpinInternalEventLoop()
     // 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);
+          ::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.
@@ -710,17 +710,17 @@ RPCChannel::SpinInternalEventLoop()
     if (result == WAIT_OBJECT_0) {
       // Our NotifyWorkerThread event was signaled
       return;
     }
   } while (true);
 }
 
 bool
-SyncChannel::WaitForNotify()
+MessageChannel::WaitForSyncNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   // Initialize global objects used in deferred messaging.
   Init();
 
   NS_ASSERTION(mTopFrame && !mTopFrame->mRPC,
                "Top frame is not a sync frame!");
@@ -737,20 +737,20 @@ SyncChannel::WaitForNotify()
 
     // 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!");
   }
 
   // Setup deferred processing of native events while we wait for a response.
-  NS_ASSERTION(!SyncChannel::IsPumpingMessages(),
+  NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
                "Shouldn't be pumping already!");
 
-  SyncChannel::SetIsPumpingMessages(true);
+  MessageChannel::SetIsPumpingMessages(true);
   HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
                                       nullptr, gUIThreadId);
   NS_ASSERTION(windowHook, "Failed to set hook!");
 
   {
     while (1) {
       MSG msg = { 0 };
       // Don't get wrapped up in here if the child connection dies.
@@ -826,29 +826,29 @@ SyncChannel::WaitForNotify()
   // someone else calls GetMessage, PeekMessage, or runs code that generates
   // a "nonqueued" message.
   ScheduleDeferredMessageRun();
 
   if (timerId) {
     KillTimer(nullptr, timerId);
   }
 
-  SyncChannel::SetIsPumpingMessages(false);
+  MessageChannel::SetIsPumpingMessages(false);
 
   return WaitResponse(timedout);
 }
 
 bool
-RPCChannel::WaitForNotify()
+MessageChannel::WaitForRPCNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
-  if (!StackDepth()) {
+  if (!RPCStackDepth()) {
     // There is currently no way to recover from this condition.
-    NS_RUNTIMEABORT("StackDepth() is 0 in call to RPCChannel::WaitForNotify!");
+    NS_RUNTIMEABORT("StackDepth() is 0 in call to MessageChannel::WaitForNotify!");
   }
 
   // Initialize global objects used in deferred messaging.
   Init();
 
   NS_ASSERTION(mTopFrame && mTopFrame->mRPC,
                "Top frame is not a sync frame!");
 
@@ -857,52 +857,52 @@ RPCChannel::WaitForNotify()
   bool timedout = false;
 
   UINT_PTR timerId = 0;
   TimeoutData timeoutData = { 0 };
 
   // windowHook 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.
-  // If windowHook is nullptr, SyncChannel::IsPumpingMessages should be false.
+  // If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false.
   HHOOK windowHook = nullptr;
 
   while (1) {
-    NS_ASSERTION((!!windowHook) == SyncChannel::IsPumpingMessages(),
+    NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(),
                  "windowHook out of sync with reality");
 
     if (mTopFrame->mSpinNestedEvents) {
       if (windowHook) {
         UnhookWindowsHookEx(windowHook);
         windowHook = nullptr;
 
         if (timerId) {
           KillTimer(nullptr, timerId);
           timerId = 0;
         }
 
         // Used by widget to assert on incoming native events
-        SyncChannel::SetIsPumpingMessages(false);
+        MessageChannel::SetIsPumpingMessages(false);
 
         // Unhook any neutered windows procedures so messages can be delievered
         // normally.
         UnhookNeuteredWindows();
 
         // Send all deferred "nonqueued" message to the intended receiver.
         // We're dropping into SpinInternalEventLoop so we should be fairly
         // certain these will get delivered soohn.
         ScheduleDeferredMessageRun();
       }
       SpinInternalEventLoop();
       ResetEvent(mEvent);
       return true;
     }
 
     if (!windowHook) {
-      SyncChannel::SetIsPumpingMessages(true);
+      MessageChannel::SetIsPumpingMessages(true);
       windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
                                     nullptr, gUIThreadId);
       NS_ASSERTION(windowHook, "Failed to set hook!");
 
       NS_ASSERTION(!timerId, "Timer already initialized?");
 
       if (mTimeoutMs != kNoTimeout) {
         InitTimeoutData(&timeoutData, mTimeoutMs);
@@ -934,17 +934,17 @@ RPCChannel::WaitForNotify()
     }
 
     if (TimeoutHasExpired(timeoutData)) {
       // A timeout was specified and we've passed it. Break out.
       timedout = true;
       break;
     }
 
-    // See SyncChannel's WaitForNotify for details.
+    // See MessageChannel's WaitFor*Notify for details.
     bool haveSentMessagesPending =
       (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
 
     // 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.
@@ -966,23 +966,23 @@ RPCChannel::WaitForNotify()
     // a "nonqueued" message.
     ScheduleDeferredMessageRun();
 
     if (timerId) {
       KillTimer(nullptr, timerId);
     }
   }
 
-  SyncChannel::SetIsPumpingMessages(false);
+  MessageChannel::SetIsPumpingMessages(false);
 
   return WaitResponse(timedout);
 }
 
 void
-SyncChannel::NotifyWorkerThread()
+MessageChannel::NotifyWorkerThread()
 {
   mMonitor->AssertCurrentThreadOwns();
   NS_ASSERTION(mEvent, "No signal event to set, this is really bad!");
   if (!SetEvent(mEvent)) {
     NS_WARNING("Failed to set NotifyWorkerThread event!");
   }
 }
 
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -7,33 +7,32 @@
 MODULE = 'ipc'
 
 EXPORTS += [
     'nsIIPCSerializableInputStream.h',
     'nsIIPCSerializableURI.h',
 ]
 
 EXPORTS.mozilla.ipc += [
-    'AsyncChannel.h',
     'BrowserProcessSubThread.h',
     'CrossProcessMutex.h',
     'FileDescriptor.h',
     'FileDescriptorUtils.h',
     'GeckoChildProcessHost.h',
     'IOThreadChild.h',
     'InputStreamUtils.h',
+    'MessageChannel.h',
+    'MessageLink.h',
     'ProcessChild.h',
     'ProtocolUtils.h',
-    'RPCChannel.h',
     'ScopedXREEmbed.h',
     'SharedMemory.h',
     'SharedMemoryBasic.h',
     'SharedMemorySysV.h',
     'Shmem.h',
-    'SyncChannel.h',
     'Transport.h',
     'URIUtils.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla.ipc += [
         'Transport_win.h',
     ]
@@ -73,31 +72,30 @@ else:
         'ProcessUtils_none.cpp',
     ]
 
 EXPORTS.ipc += [
     'IPCMessageUtils.h',
 ]
 
 CPP_SOURCES += [
-    'AsyncChannel.cpp',
     'BrowserProcessSubThread.cpp',
     'FileDescriptor.cpp',
     'FileDescriptorUtils.cpp',
     'GeckoChildProcessHost.cpp',
     'InputStreamUtils.cpp',
+    'MessageChannel.cpp',
+    'MessageLink.cpp',
     'MessagePump.cpp',
     'ProcessChild.cpp',
     'ProtocolUtils.cpp',
-    'RPCChannel.cpp',
     'ScopedXREEmbed.cpp',
     'SharedMemory.cpp',
     'Shmem.cpp',
     'StringUtil.cpp',
-    'SyncChannel.cpp',
     'URIUtils.cpp',
 ]
 
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
     'URIParams.ipdlh',
 ]
 
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -272,23 +272,23 @@ def _putInNamespaces(cxxthing, namespace
         newns = Namespace(ns.name)
         innerns.addstmt(newns)
         innerns = newns
     innerns.addstmt(cxxthing)
     return outerns
 
 def _sendPrefix(msgtype):
     """Prefix of the name of the C++ method that sends |msgtype|."""
-    if msgtype.isRpc():
+    if msgtype.isRpc() or msgtype.isUrgent():
         return 'Call'
     return 'Send'
 
 def _recvPrefix(msgtype):
     """Prefix of the name of the C++ method that handles |msgtype|."""
-    if msgtype.isRpc():
+    if msgtype.isRpc() or msgtype.isUrgent():
         return 'Answer'
     return 'Recv'
 
 def _flatTypeName(ipdltype):
     """Return a 'flattened' IPDL type name that can be used as an
 identifier.
 E.g., |Foo[]| --> |ArrayOfFoo|."""
     # NB: this logic depends heavily on what IPDL types are allowed to
@@ -976,25 +976,17 @@ class MessageDecl(ipdl.ast.MessageDecl):
                     ipdl.type.ActorType(
                         messageDecl.decl.type.constructedType()),
                     'actor'))
         messageDecl.__class__ = MessageDecl
         return messageDecl
 
 ##--------------------------------------------------
 def _semsToChannelParts(sems):
-    if ipdl.ast.ASYNC == sems:   channel = 'AsyncChannel'
-    elif ipdl.ast.SYNC == sems:  channel = 'SyncChannel'
-    elif ipdl.ast.RPC == sems:   channel = 'RPCChannel'
-    return [ 'mozilla', 'ipc', channel ]
-
-def _semsToListener(sems):
-    return { ipdl.ast.ASYNC: 'AsyncListener',
-             ipdl.ast.SYNC: 'SyncListener',
-             ipdl.ast.RPC: 'RPCListener' }[sems]
+    return [ 'mozilla', 'ipc', 'MessageChannel' ]
 
 def _usesShmem(p):
     for md in p.messageDecls:
         for param in md.inParams:
             if ipdl.type.hasshmem(param.type):
                 return True
         for ret in md.outParams:
             if ipdl.type.hasshmem(ret.type):
@@ -1027,21 +1019,18 @@ class Protocol(ipdl.ast.Protocol):
         return '->'
 
     def channelType(self):
         return Type('Channel', ptr=not self.decl.type.isToplevel())
 
     def channelHeaderFile(self):
         return '/'.join(_semsToChannelParts(self.sendSems())) +'.h'
 
-    def listenerName(self):
-        return _semsToListener(self.sendSems())
-
     def fqListenerName(self):
-        return self.channelName() +'::'+ _semsToListener(self.sendSems())
+      return 'mozilla::ipc::MessageListener'
 
     def managerInterfaceType(self, ptr=0):
         return Type('mozilla::ipc::IProtocolManager',
                     ptr=ptr,
                     T=Type(self.fqListenerName()))
 
     def _ipdlmgrtype(self):
         assert 1 == len(self.decl.type.managers)
@@ -1560,24 +1549,24 @@ class _GenerateProtocolCode(ipdl.ast.Vis
 
         tfDecl, tfDefn = _splitFuncDeclDefn(self.genTransitionFunc())
         ns.addstmts([ tfDecl, Whitespace.NL ])
         self.funcDefns.append(tfDefn)
 
         typedefs = self.protocol.decl.cxxtypedefs
         for md in p.messageDecls:
             ns.addstmts([
-                _generateMessageClass(md, md.msgClass(), md.msgId(),
+                _generateMessageClass(md.msgClass(), md.msgId(),
                                       typedefs, md.prettyMsgName(p.name+'::'),
                                       md.decl.type.compress),
                 Whitespace.NL ])
             if md.hasReply():
                 ns.addstmts([
                     _generateMessageClass(
-                        md, md.replyClass(), md.replyId(),
+                        md.replyClass(), md.replyId(),
                         typedefs, md.prettyReplyName(p.name+'::'),
                         md.decl.type.compress),
                     Whitespace.NL ])
 
         ns.addstmts([ Whitespace.NL, Whitespace.NL ])
 
 
     def genBridgeFunc(self, bridge):
@@ -1759,43 +1748,39 @@ class _GenerateProtocolCode(ipdl.ast.Vis
             # all --> Error transitions break to here
             StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
             StmtReturn(ExprLiteral.FALSE)
         ])
         return transitionfunc
 
 ##--------------------------------------------------
 
-def _generateMessageClass(md, clsname, msgid, typedefs, prettyName, compress):
+def _generateMessageClass(clsname, msgid, typedefs, prettyName, compress):
     cls = Class(name=clsname, inherits=[ Inherit(Type('IPC::Message')) ])
     cls.addstmt(Label.PRIVATE)
     cls.addstmts(typedefs)
     cls.addstmt(Whitespace.NL)
 
     cls.addstmt(Label.PUBLIC)
 
     idenum = TypeEnum()
     idenum.addId('ID', msgid)
     cls.addstmt(StmtDecl(Decl(idenum, '')))
 
     # make the message constructor
     if compress:
         compression = ExprVar('COMPRESSION_ENABLED')
     else:
         compression = ExprVar('COMPRESSION_NONE')
-    if md.decl.type.isUrgent():
-        priority = 'PRIORITY_HIGH'
-    else:
-        priority = 'PRIORITY_NORMAL'
     ctor = ConstructorDefn(
         ConstructorDecl(clsname),
         memberinits=[ ExprMemberInit(ExprVar('IPC::Message'),
                                      [ ExprVar('MSG_ROUTING_NONE'),
                                        ExprVar('ID'),
-                                       ExprVar(priority),
+                                       ExprVar('IPC::Message::PRIORITY_NORMAL'),
                                        compression,
                                        ExprLiteral.String(prettyName) ]) ])
     cls.addstmts([ ctor, Whitespace.NL ])
 
     # generate a logging function
     # 'pfx' will be something like "[FooParent] sent"
     pfxvar = ExprVar('__pfx')
     outfvar = ExprVar('__outf')
@@ -2466,17 +2451,17 @@ class _GenerateProtocolActorCode(ipdl.as
         tu.accept(self)
 
     def standardTypedefs(self):
         return [
             Typedef(Type('IPC::Message'), 'Message'),
             Typedef(Type(self.protocol.channelName()), 'Channel'),
             Typedef(Type(self.protocol.fqListenerName()), 'ChannelListener'),
             Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
-            Typedef(Type('mozilla::ipc::AsyncChannel'), 'AsyncChannel'),
+            Typedef(Type('mozilla::ipc::MessageChannel'), 'MessageChannel'),
             Typedef(Type('mozilla::ipc::SharedMemory'), 'SharedMemory'),
             Typedef(Type('mozilla::ipc::Trigger'), 'Trigger')
         ]
 
 
     def visitTranslationUnit(self, tu):
         self.protocol = tu.protocol
 
@@ -2822,44 +2807,44 @@ class _GenerateProtocolActorCode(ipdl.as
                 MethodDecl(
                     'Open',
                     params=[ Decl(Type('Channel::Transport', ptr=True),
                                       aTransportVar.name),
                              Decl(Type('ProcessHandle'), processvar.name),
                              Param(Type('MessageLoop', ptr=True),
                                    aThreadVar.name,
                                    default=ExprLiteral.NULL),
-                             Param(Type('AsyncChannel::Side'),
+                             Param(Type('mozilla::ipc::Side'),
                                    sidevar.name,
-                                   default=ExprVar('Channel::Unknown')) ],
+                                   default=ExprVar('mozilla::ipc::UnknownSide')) ],
                     ret=Type.BOOL))
 
             openmeth.addstmts([
                 StmtExpr(ExprAssn(p.otherProcessVar(), processvar)),
                 StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
                                     [ aTransportVar, aThreadVar, sidevar ]))
             ])
             self.cls.addstmts([
                 openmeth,
                 Whitespace.NL ])
 
-            # Open(AsyncChannel *, MessageLoop *, Side)
+            # Open(MessageChannel *, MessageLoop *, Side)
             aChannel = ExprVar('aChannel')
             aMessageLoop = ExprVar('aMessageLoop')
             sidevar = ExprVar('aSide')
             openmeth = MethodDefn(
                 MethodDecl(
                     'Open',
-                    params=[ Decl(Type('AsyncChannel', ptr=True),
+                    params=[ Decl(Type('MessageChannel', ptr=True),
                                       aChannel.name),
                              Param(Type('MessageLoop', ptr=True),
                                    aMessageLoop.name),
-                             Param(Type('AsyncChannel::Side'),
+                             Param(Type('mozilla::ipc::Side'),
                                    sidevar.name,
-                                   default=ExprVar('Channel::Unknown')) ],
+                                   default=ExprVar('mozilla::ipc::UnknownSide')) ],
                     ret=Type.BOOL))
 
             openmeth.addstmts([
                 StmtExpr(ExprAssn(p.otherProcessVar(), ExprCall(ExprVar('base::GetCurrentProcessHandle')))),
                 StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
                                     [ aChannel, aMessageLoop, sidevar ]))
             ])
             self.cls.addstmts([
@@ -2974,16 +2959,23 @@ class _GenerateProtocolActorCode(ipdl.as
         def makeHandlerMethod(name, switch, hasReply, dispatches=0):
             params = [ Decl(Type('Message', const=1, ref=1), msgvar.name) ]
             if hasReply:
                 params.append(Decl(Type('Message', ref=1, ptr=1),
                                    replyvar.name))
             
             method = MethodDefn(MethodDecl(name, virtual=True,
                                            params=params, ret=_Result.Type()))
+
+            if not switch:
+              crash = StmtExpr(ExprCall(ExprVar('MOZ_ASSUME_UNREACHABLE'),
+                               args=[ExprLiteral.String('message protocol not supported')]))
+              method.addstmts([crash, StmtReturn(_Result.NotKnown)])
+              return method
+
             if dispatches:
                 routevar = ExprVar('__route')
                 routedecl = StmtDecl(
                     Decl(_actorIdType(), routevar.name),
                     init=ExprCall(ExprSelect(msgvar, '.', 'routing_id')))
 
                 routeif = StmtIf(ExprBinary(
                     ExprVar('MSG_ROUTING_CONTROL'), '!=', routevar))
@@ -3027,28 +3019,30 @@ class _GenerateProtocolActorCode(ipdl.as
             return method
 
         dispatches = (ptype.isToplevel() and ptype.isManager())
         self.cls.addstmts([
             makeHandlerMethod('OnMessageReceived', self.asyncSwitch,
                               hasReply=0, dispatches=dispatches),
             Whitespace.NL
         ])
-        if toplevel.talksSync():
-            self.cls.addstmts([
-                makeHandlerMethod('OnMessageReceived', self.syncSwitch,
-                                  hasReply=1, dispatches=dispatches),
-                Whitespace.NL
-            ])
-            if toplevel.talksRpc():
-                self.cls.addstmts([
-                    makeHandlerMethod('OnCallReceived', self.rpcSwitch,
-                                      hasReply=1, dispatches=dispatches),
-                    Whitespace.NL
-                ])
+        if not toplevel.talksRpc():
+          self.rpcSwitch = None
+          if not toplevel.talksSync():
+            self.syncSwitch = None
+        self.cls.addstmts([
+            makeHandlerMethod('OnMessageReceived', self.syncSwitch,
+                              hasReply=1, dispatches=dispatches),
+            Whitespace.NL
+        ])
+        self.cls.addstmts([
+            makeHandlerMethod('OnCallReceived', self.rpcSwitch,
+                              hasReply=1, dispatches=dispatches),
+            Whitespace.NL
+        ])
 
         destroysubtreevar = ExprVar('DestroySubtree')
         deallocsubtreevar = ExprVar('DeallocSubtree')
         deallocshmemvar = ExprVar('DeallocShmems')
 
         # OnProcesingError(code)
         codevar = ExprVar('code')
         onprocessingerror = MethodDefn(
@@ -3080,17 +3074,17 @@ class _GenerateProtocolActorCode(ipdl.as
                 ontimeout.addstmts([
                     _runtimeAbort("`OnReplyTimeout' called on non-toplevel actor"),
                     StmtReturn.FALSE
                 ])
 
             self.cls.addstmts([ ontimeout, Whitespace.NL ])
 
         # C++-stack-related methods
-        if ptype.isToplevel() and toplevel.talksRpc():
+        if ptype.isToplevel():
             # OnEnteredCxxStack()
             onentered = MethodDefn(MethodDecl('OnEnteredCxxStack'))
             onentered.addstmt(StmtReturn(ExprCall(p.enteredCxxStackVar())))
 
             # OnExitedCxxStack()
             onexited = MethodDefn(MethodDecl('OnExitedCxxStack'))
             onexited.addstmt(StmtReturn(ExprCall(p.exitedCxxStackVar())))
 
@@ -3465,17 +3459,17 @@ class _GenerateProtocolActorCode(ipdl.as
 
         otherprocess = MethodDefn(MethodDecl(
             p.otherProcessMethod().name,
             ret=Type('ProcessHandle'),
             const=1,
             virtual=1))
         getchannel = MethodDefn(MethodDecl(
             p.getChannelMethod().name,
-            ret=Type('AsyncChannel', ptr=1),
+            ret=Type('MessageChannel', ptr=1),
             virtual=1))
 
         if p.decl.type.isToplevel():
             tmpvar = ExprVar('tmp')
             
             register.addstmts([
                 StmtDecl(Decl(_actorIdType(), tmpvar.name),
                          p.nextActorIdExpr(self.side)),
@@ -4904,16 +4898,19 @@ class _GenerateProtocolActorCode(ipdl.as
         
         stmts = [ StmtExpr(ExprCall(
             ExprSelect(var, '->', 'set_routing_id'),
             args=[ routingId ])) ]
 
         if md.decl.type.isSync():
             stmts.append(StmtExpr(ExprCall(
                 ExprSelect(var, '->', 'set_sync'))))
+        elif md.decl.type.isUrgent():
+            stmts.append(StmtExpr(ExprCall(
+                ExprSelect(var, '->', 'set_urgent'))))
         elif md.decl.type.isRpc():
             stmts.append(StmtExpr(ExprCall(
                 ExprSelect(var, '->', 'set_rpc'))))
 
         if reply:
             stmts.append(StmtExpr(ExprCall(
                 ExprSelect(var, '->', 'set_reply'))))
 
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -199,24 +199,24 @@ class IPDLType(Type):
     def isAtom(self):  return True
     def isCompound(self): return False
     def isShmem(self): return False
     def isChmod(self): return False
     def isFD(self): return False
 
     def isAsync(self): return self.sendSemantics is ASYNC
     def isSync(self): return self.sendSemantics is SYNC
-    def isRpc(self): return self.sendSemantics is RPC or self.sendSemantics is URGENT
+    def isRpc(self): return self.sendSemantics is RPC
     def isUrgent(self): return self.sendSemantics is URGENT
 
     def talksAsync(self): return True
     def talksSync(self): return self.isSync() or self.isRpc()
     def talksRpc(self): return self.isRpc()
 
-    def hasReply(self):  return self.isSync() or self.isRpc()
+    def hasReply(self):  return self.isSync() or self.isRpc() or self.isUrgent()
 
     def needsMoreJuiceThan(self, o):
         return (o.isAsync() and not self.isAsync()
                 or o.isSync() and self.isRpc())
 
 class StateType(IPDLType):
     def __init__(self, protocol, name, start=False):
         self.protocol = protocol
@@ -1454,16 +1454,22 @@ class CheckTypes(TcheckVisitor):
         loc = md.decl.loc
 
         if mtype.isSync() and (mtype.isOut() or mtype.isInout()):
             self.error(
                 loc,
                 "sync parent-to-child messages are verboten (here, message `%s' in protocol `%s')",
                 mname, pname)
 
+        if mtype.isUrgent() and (mtype.isIn() or mtype.isInout()):
+            self.error(
+                loc,
+                "urgent child-to-parent messages are verboten (here, message `%s' in protocol `%s')",
+                mname, pname)
+
         if mtype.needsMoreJuiceThan(ptype):
             self.error(
                 loc,
                 "message `%s' requires more powerful send semantics than its protocol `%s' provides",
                 mname, pname)
 
         if mtype.isAsync() and len(mtype.returns):
             # XXX/cjones could modify grammar to disallow this ...
--- a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
@@ -1,13 +1,15 @@
 include protocol PTestDataStructuresSub;
 include PTestDataStructuresCommon;
 
 include "mozilla/GfxMessageUtils.h";
 
+include "mozilla/GfxMessageUtils.h";
+
 namespace mozilla {
 namespace _ipdltest {
 
 sync protocol PTestDataStructures {
     manages PTestDataStructuresSub;
 
 child:
     PTestDataStructuresSub(int i);
--- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp
+++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp
@@ -30,17 +30,17 @@ TestBridgeMainParent::AllocPTestBridgeMa
                                                     ProcessId otherProcess)
 {
     ProcessHandle h;
     if (!base::OpenProcessHandle(otherProcess, &h)) {
         return nullptr;
     }
 
     nsAutoPtr<TestBridgeMainSubParent> a(new TestBridgeMainSubParent(transport));
-    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), AsyncChannel::Parent)) {
+    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ParentSide)) {
         return nullptr;
     }
     a.forget();
     return true;
 }
 
 void
 TestBridgeMainParent::ActorDestroy(ActorDestroyReason why)
@@ -182,17 +182,17 @@ TestBridgeSubChild::AllocPTestBridgeMain
                                                  ProcessId otherProcess)
 {
     ProcessHandle h;
     if (!base::OpenProcessHandle(otherProcess, &h)) {
         return false;
     }
 
     nsAutoPtr<TestBridgeMainSubChild> a(new TestBridgeMainSubChild(transport));
-    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), AsyncChannel::Child)) {
+    if (!a->Open(transport, h, XRE_GetIOMessageLoop(), ipc::ChildSide)) {
         return false;
     }
 
     if (!a->SendHello())
         fail("sending Hello");
 
     a.forget();
     return true;
--- a/ipc/ipdl/test/cxx/TestOpens.cpp
+++ b/ipc/ipdl/test/cxx/TestOpens.cpp
@@ -55,17 +55,17 @@ OpenParent(TestOpensOpenedParent* aParen
            Transport* aTransport, ProcessHandle aOtherProcess)
 {
     AssertNotMainThread();
 
     // Open the actor on the off-main thread to park it there.
     // Messages will be delivered to this thread's message loop
     // instead of the main thread's.
     if (!aParent->Open(aTransport, aOtherProcess,
-                       XRE_GetIOMessageLoop(), AsyncChannel::Parent))
+                       XRE_GetIOMessageLoop(), ipc::ParentSide))
         fail("opening Parent");
 }
 
 bool
 TestOpensParent::AllocPTestOpensOpenedParent(Transport* transport,
                                              ProcessId otherProcess)
 {
     gMainThread = MessageLoop::current();
@@ -164,17 +164,17 @@ OpenChild(TestOpensOpenedChild* aChild,
            Transport* aTransport, ProcessHandle aOtherProcess)
 {
     AssertNotMainThread();
 
     // Open the actor on the off-main thread to park it there.
     // Messages will be delivered to this thread's message loop
     // instead of the main thread's.
     if (!aChild->Open(aTransport, aOtherProcess,
-                      XRE_GetIOMessageLoop(), AsyncChannel::Child))
+                      XRE_GetIOMessageLoop(), ipc::ChildSide))
         fail("opening Child");
 
     // Kick off the unit tests
     if (!aChild->SendHello())
         fail("sending Hello");
 }
 
 bool
--- a/ipc/ipdl/test/cxx/TestRPCRaces.cpp
+++ b/ipc/ipdl/test/cxx/TestRPCRaces.cpp
@@ -1,31 +1,31 @@
 #include "TestRPCRaces.h"
 
 #include "IPDLUnitTests.h"      // fail etc.
 
-using mozilla::ipc::RPCChannel;
+using mozilla::ipc::MessageChannel;
 
 template<>
 struct RunnableMethodTraits<mozilla::_ipdltest::TestRPCRacesParent>
 {
     static void RetainCallee(mozilla::_ipdltest::TestRPCRacesParent* obj) { }
     static void ReleaseCallee(mozilla::_ipdltest::TestRPCRacesParent* obj) { }
 };
 
 
 namespace mozilla {
 namespace _ipdltest {
 
-RPCChannel::RacyRPCPolicy
-MediateRace(const RPCChannel::Message& parent,
-            const RPCChannel::Message& child)
+ipc::RacyRPCPolicy
+MediateRace(const MessageChannel::Message& parent,
+            const MessageChannel::Message& child)
 {
     return (PTestRPCRaces::Msg_Child__ID == parent.type()) ?
-        RPCChannel::RRPParentWins : RPCChannel::RRPChildWins;
+        ipc::RRPParentWins : ipc::RRPChildWins;
 }
 
 //-----------------------------------------------------------------------------
 // parent
 void
 TestRPCRacesParent::Main()
 {
     if (!SendStart())
--- a/ipc/ipdl/test/cxx/TestRPCRaces.h
+++ b/ipc/ipdl/test/cxx/TestRPCRaces.h
@@ -4,19 +4,19 @@
 #include "mozilla/_ipdltest/IPDLUnitTests.h"
 
 #include "mozilla/_ipdltest/PTestRPCRacesParent.h"
 #include "mozilla/_ipdltest/PTestRPCRacesChild.h"
 
 namespace mozilla {
 namespace _ipdltest {
 
-mozilla::ipc::RPCChannel::RacyRPCPolicy
-MediateRace(const mozilla::ipc::RPCChannel::Message& parent,
-            const mozilla::ipc::RPCChannel::Message& child);
+mozilla::ipc::RacyRPCPolicy
+MediateRace(const mozilla::ipc::MessageChannel::Message& parent,
+            const mozilla::ipc::MessageChannel::Message& child);
 
 class TestRPCRacesParent :
     public PTestRPCRacesParent
 {
 public:
     TestRPCRacesParent() : mHasReply(false),
                            mChildHasReply(false),
                            mAnsweredParent(false)
@@ -42,17 +42,17 @@ protected:
     AnswerStackFrame3() MOZ_OVERRIDE;
 
     virtual bool
     AnswerParent() MOZ_OVERRIDE;
 
     virtual bool
     RecvGetAnsweredParent(bool* answeredParent) MOZ_OVERRIDE;
 
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE
     {
         if (NormalShutdown != why)
@@ -99,17 +99,17 @@ protected:
     RecvWakeup() MOZ_OVERRIDE;
 
     virtual bool
     RecvWakeup3() MOZ_OVERRIDE;
 
     virtual bool
     AnswerChild() MOZ_OVERRIDE;
 
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE
     {
         if (NormalShutdown != why)
--- a/ipc/ipdl/test/cxx/TestRaceDeferral.cpp
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.cpp
@@ -1,24 +1,23 @@
 #include "TestRaceDeferral.h"
 
 #include "IPDLUnitTests.h"      // fail etc.
 
 using namespace mozilla::ipc;
-typedef mozilla::ipc::RPCChannel::Message Message;
-typedef mozilla::ipc::RPCChannel::RacyRPCPolicy RacyRPCPolicy;
+typedef mozilla::ipc::MessageChannel::Message Message;
 
 namespace mozilla {
 namespace _ipdltest {
 
 static RacyRPCPolicy
 MediateRace(const Message& parent, const Message& child)
 {
     return (PTestRaceDeferral::Msg_Win__ID == parent.type()) ?
-        RPCChannel::RRPParentWins : RPCChannel::RRPChildWins;
+        RRPParentWins : RRPChildWins;
 }
 
 //-----------------------------------------------------------------------------
 // parent
 
 TestRaceDeferralParent::TestRaceDeferralParent()
     : mProcessedLose(false)
 {
--- a/ipc/ipdl/test/cxx/TestRaceDeferral.h
+++ b/ipc/ipdl/test/cxx/TestRaceDeferral.h
@@ -21,17 +21,17 @@ public:
 
     void Main();
 
 protected:
     void Test1();
 
     virtual bool AnswerLose() MOZ_OVERRIDE;
 
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE
     {
         if (NormalShutdown != why)
             fail("unexpected destruction!");  
         passed("ok");
         QuitParent();
@@ -50,17 +50,17 @@ public:
 
 protected:
     virtual bool RecvStartRace() MOZ_OVERRIDE;
 
     virtual bool AnswerWin() MOZ_OVERRIDE;
 
     virtual bool AnswerRpc() MOZ_OVERRIDE;
 
-    virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
+    virtual mozilla::ipc::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child) MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE
     {
         if (NormalShutdown != why)
             fail("unexpected destruction!");
         QuitChild();
     }
--- a/ipc/ipdl/test/cxx/TestStackHooks.cpp
+++ b/ipc/ipdl/test/cxx/TestStackHooks.cpp
@@ -75,17 +75,17 @@ TestStackHooksChild::RecvStart()
 {
     if (!mOnStack)
         fail("missed stack notification");
 
     if (0 != mIncallDepth)
         fail("EnteredCall/ExitedCall malfunction");
 
     // kick off tests from a runnable so that we can start with
-    // RPCChannel code on the C++ stack
+    // MessageChannel code on the C++ stack
     MessageLoop::current()->PostTask(FROM_HERE,
                                      NewRunnableFunction(RunTestsFn));
 
     return true;
 }
 
 bool
 TestStackHooksChild::AnswerStackFrame()
--- a/ipc/ipdl/test/cxx/TestUrgency.cpp
+++ b/ipc/ipdl/test/cxx/TestUrgency.cpp
@@ -1,23 +1,34 @@
 #include "TestUrgency.h"
 
 #include "IPDLUnitTests.h"      // fail etc.
 #include <unistd.h>
+#if defined(OS_POSIX)
+#else
+#include <windows.h>
+#endif
 
 template<>
 struct RunnableMethodTraits<mozilla::_ipdltest::TestUrgencyParent>
 {
     static void RetainCallee(mozilla::_ipdltest::TestUrgencyParent* obj) { }
     static void ReleaseCallee(mozilla::_ipdltest::TestUrgencyParent* obj) { }
 };
 
 namespace mozilla {
 namespace _ipdltest {
 
+#if defined(OS_POSIX)
+static void Sleep(int ms)
+{
+    sleep(ms / 1000);
+}
+#endif
+
 //-----------------------------------------------------------------------------
 // parent
 
 TestUrgencyParent::TestUrgencyParent()
   : inreply_(false)
 {
     MOZ_COUNT_CTOR(TestUrgencyParent);
 }
@@ -159,17 +170,17 @@ TestUrgencyChild::AnswerReply1(uint32_t 
 
 bool
 TestUrgencyChild::AnswerReply2(uint32_t *reply)
 {
   if (test_ != kSecondTestBegin)
     fail("wrong test # in AnswerReply2");
 
   // sleep for 5 seconds so the parent process tries to deliver more messages.
-  sleep(5);
+  Sleep(5000);
 
   *reply = 500;
   test_ = kSecondTestGotReply;
   return true;
 }
 
 bool
 TestUrgencyChild::AnswerTest4_Reenter()
--- a/ipc/ipdl/test/cxx/genIPDLUnitTests.py
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -89,19 +89,19 @@ def main(argv):
         %sParent** parent =
         reinterpret_cast<%sParent**>(&gParentActor);
         *parent = new %sParent();
 
         %sChild** child =
         reinterpret_cast<%sChild**>(&gChildActor);
         *child = new %sChild();
 
-        ::mozilla::ipc::AsyncChannel *childChannel = (*child)->GetIPCChannel();
-        ::mozilla::ipc::AsyncChannel::Side parentSide = 
-            ::mozilla::ipc::AsyncChannel::Parent;
+        ::mozilla::ipc::MessageChannel *childChannel = (*child)->GetIPCChannel();
+        ::mozilla::ipc::Side parentSide =
+            ::mozilla::ipc::ParentSide;
 
         (*parent)->Open(childChannel, childMessageLoop, parentSide);
         return (*parent)->Main();
         }
 '''% (t, t, t, t, t, t, t) for t in unittests ])
 
     child_delete_cases = '\n'.join([
 '''    case %s: {
--- a/widget/windows/nsAppShell.cpp
+++ b/widget/windows/nsAppShell.cpp
@@ -1,14 +1,14 @@
 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.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/RPCChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "nsAppShell.h"
 #include "nsToolkit.h"
 #include "nsThreadUtils.h"
 #include "WinUtils.h"
 #include "WinTaskbar.h"
 #include "WinMouseScrollHandler.h"
 #include "nsWindowDefs.h"
 #include "nsString.h"
@@ -170,17 +170,17 @@ nsAppShell::ScheduleNativeEventCallback(
   }
   ::PostMessage(mEventWnd, sAppShellGeckoMsgId, 0, reinterpret_cast<LPARAM>(this));
 }
 
 bool
 nsAppShell::ProcessNextNativeEvent(bool mayWait)
 {
   // Notify ipc we are spinning a (possibly nested) gecko event loop.
-  mozilla::ipc::RPCChannel::NotifyGeckoEventDispatch();
+  mozilla::ipc::MessageChannel::NotifyGeckoEventDispatch();
 
   bool gotMessage = false;
 
   do {
     MSG msg;
     bool uiMessage = false;
 
     // For avoiding deadlock between our process and plugin process by
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -55,17 +55,17 @@
  **************************************************************/
 
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/Util.h"
 
-#include "mozilla/ipc/RPCChannel.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include <algorithm>
 
 #include "nsWindow.h"
 
 #include <shellapi.h>
 #include <windows.h>
 #include <process.h>
 #include <commctrl.h>
@@ -4187,21 +4187,21 @@ nsWindow::IsAsyncResponseEvent(UINT aMsg
 #endif
 
   return false;
 }
 
 void
 nsWindow::IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam)
 {
-  NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(),
+  NS_ASSERTION(!mozilla::ipc::MessageChannel::IsPumpingMessages(),
                "Failed to prevent a nonqueued message from running!");
 
   // Modal UI being displayed in windowless plugins.
-  if (mozilla::ipc::RPCChannel::IsSpinLoopActive() &&
+  if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
       (InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
     LRESULT res;
     if (IsAsyncResponseEvent(msg, res)) {
       ReplyMessage(res);
     }
     return;
   }
 
--- a/widget/windows/nsWindowGfx.cpp
+++ b/widget/windows/nsWindowGfx.cpp
@@ -172,17 +172,17 @@ nsIWidgetListener* nsWindow::GetPaintLis
 }
 
 bool nsWindow::OnPaint(HDC aDC, uint32_t aNestingLevel)
 {
   // We never have reentrant paint events, except when we're running our RPC
   // windows event spin loop. If we don't trap for this, we'll try to paint,
   // but view manager will refuse to paint the surface, resulting is black
   // flashes on the plugin rendering surface.
-  if (mozilla::ipc::RPCChannel::IsSpinLoopActive() && mPainting)
+  if (mozilla::ipc::MessageChannel::IsSpinLoopActive() && mPainting)
     return false;
 
   if (mWindowType == eWindowType_plugin) {
 
     /**
      * After we CallUpdateWindow to the child, occasionally a WM_PAINT message
      * is posted to the parent event loop with an empty update rect. Do a
      * dummy paint so that Windows stops dispatching WM_PAINT in an inifinite
--- a/widget/xpwidgets/nsBaseWidget.cpp
+++ b/widget/xpwidgets/nsBaseWidget.cpp
@@ -54,19 +54,19 @@ static void debug_RegisterPrefCallbacks(
 
 #ifdef NOISY_WIDGET_LEAKS
 static int32_t gNumWidgets;
 #endif
 
 nsIRollupListener* nsBaseWidget::gRollupListener = nullptr;
 
 using namespace mozilla::layers;
+using namespace mozilla::ipc;
 using namespace mozilla;
 using base::Thread;
-using mozilla::ipc::AsyncChannel;
 
 nsIContent* nsBaseWidget::mLastRollup = nullptr;
 // Global user preference for disabling native theme. Used
 // in NativeWindowTheme.
 bool            gDisableNativeTheme               = false;
 
 // nsBaseWidget
 NS_IMPL_ISUPPORTS1(nsBaseWidget, nsIWidget)
@@ -945,22 +945,21 @@ void nsBaseWidget::CreateCompositor(int 
 
   // If we've already received a shutdown notification, don't try
   // create a new compositor.
   if (!mShutdownObserver) {
     return;
   }
 
   mCompositorParent = NewCompositorParent(aWidth, aHeight);
-  AsyncChannel *parentChannel = mCompositorParent->GetIPCChannel();
+  MessageChannel *parentChannel = mCompositorParent->GetIPCChannel();
   LayerManager* lm = new ClientLayerManager(this);
   MessageLoop *childMessageLoop = CompositorParent::CompositorLoop();
   mCompositorChild = new CompositorChild(lm);
-  AsyncChannel::Side childSide = mozilla::ipc::AsyncChannel::Child;
-  mCompositorChild->Open(parentChannel, childMessageLoop, childSide);
+  mCompositorChild->Open(parentChannel, childMessageLoop, ipc::ChildSide);
 
   TextureFactoryIdentifier textureFactoryIdentifier;
   PLayerTransactionChild* shadowManager;
   nsTArray<LayersBackend> backendHints;
   GetPreferredCompositorBackends(backendHints);
 
   CheckForBasicBackends(backendHints);