bug 523761: give top-level IPDL actors handles to each others' processes. r=bent
authorChris Jones <jones.chris.g@gmail.com>
Tue, 27 Oct 2009 16:52:37 -0500
changeset 36016 84c7cc1f5cb6da1c679837f677b1981a9a8e3037
parent 36015 80a3ac9febb75f8798322fa280aef665a319efcd
child 36017 704e1ac3be4a195712a745913f958bc23fff931c
push idunknown
push userunknown
push dateunknown
reviewersbent
bugs523761
milestone1.9.3a1pre
bug 523761: give top-level IPDL actors handles to each others' processes. r=bent
dom/ipc/ContentProcessChild.cpp
dom/ipc/ContentProcessChild.h
dom/ipc/ContentProcessParent.cpp
dom/ipc/ContentProcessThread.cpp
dom/ipc/ContentProcessThread.h
dom/plugins/PluginModuleChild.cpp
dom/plugins/PluginModuleChild.h
dom/plugins/PluginModuleParent.cpp
dom/plugins/PluginProcessParent.h
dom/plugins/PluginThreadChild.cpp
dom/plugins/PluginThreadChild.h
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/GeckoChildProcessHost.h
ipc/glue/GeckoThread.h
ipc/glue/ProtocolUtils.h
ipc/ipdl/ipdl/lower.py
ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.cpp
ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.h
ipc/ipdl/test/cxx/IPDLUnitTests.h
ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
ipc/ipdl/test/cxx/genIPDLUnitTests.py
ipc/test-harness/TestThreadChild.cpp
ipc/test-harness/TestThreadChild.h
toolkit/xre/nsEmbedFunctions.cpp
--- a/dom/ipc/ContentProcessChild.cpp
+++ b/dom/ipc/ContentProcessChild.cpp
@@ -60,21 +60,23 @@ ContentProcessChild::ContentProcessChild
 {
 }
 
 ContentProcessChild::~ContentProcessChild()
 {
 }
 
 bool
-ContentProcessChild::Init(MessageLoop* aIOLoop, IPC::Channel* aChannel)
+ContentProcessChild::Init(MessageLoop* aIOLoop,
+                          base::ProcessHandle aParentHandle,
+                          IPC::Channel* aChannel)
 {
     NS_ASSERTION(!sSingleton, "only one ContentProcessChild per child");
   
-    Open(aChannel, aIOLoop);
+    Open(aChannel, aParentHandle, aIOLoop);
     sSingleton = this;
 
     return true;
 }
 
 PIFrameEmbeddingChild*
 ContentProcessChild::AllocPIFrameEmbedding(const MagicWindowHandle& hwnd)
 {
--- a/dom/ipc/ContentProcessChild.h
+++ b/dom/ipc/ContentProcessChild.h
@@ -48,17 +48,19 @@ namespace mozilla {
 namespace dom {
 
 class ContentProcessChild : public PContentProcessChild
 {
 public:
     ContentProcessChild();
     virtual ~ContentProcessChild();
 
-    bool Init(MessageLoop* aIOLoop, IPC::Channel* aChannel);
+    bool Init(MessageLoop* aIOLoop,
+              base::ProcessHandle aParentHandle,
+              IPC::Channel* aChannel);
 
     static ContentProcessChild* GetSingleton() {
         NS_ASSERTION(sSingleton, "not initialized");
         return sSingleton;
     }
 
     virtual PIFrameEmbeddingChild* AllocPIFrameEmbedding(
             const MagicWindowHandle& hwnd);
--- a/dom/ipc/ContentProcessParent.cpp
+++ b/dom/ipc/ContentProcessParent.cpp
@@ -97,17 +97,17 @@ ContentProcessParent::CreateTestShell()
 
 ContentProcessParent::ContentProcessParent()
     : mMonitor("ContentProcessParent::mMonitor")
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     // TODO: async launching!
     mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, this);
     mSubprocess->SyncLaunch();
-    Open(mSubprocess->GetChannel());
+    Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle());
 }
 
 ContentProcessParent::~ContentProcessParent()
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     NS_ASSERTION(gSingleton == this, "More than one singleton?!");
     gSingletonDied = PR_TRUE;
     gSingleton = nsnull;
--- a/dom/ipc/ContentProcessThread.cpp
+++ b/dom/ipc/ContentProcessThread.cpp
@@ -46,35 +46,35 @@
 #include "chrome/common/child_process.h"
 #include "chrome/common/chrome_switches.h"
 
 using mozilla::ipc::GeckoThread;
 
 namespace mozilla {
 namespace dom {
 
-ContentProcessThread::ContentProcessThread() :
-    GeckoThread(),
+ContentProcessThread::ContentProcessThread(ProcessHandle mParentHandle) :
+    GeckoThread(mParentHandle),
     mContentProcess()
 {
 }
 
 ContentProcessThread::~ContentProcessThread()
 {
 }
 
 void
 ContentProcessThread::Init()
 {
     GeckoThread::Init();
 
     // FIXME/cjones: set up channel stuff, etc.
     
     // FIXME owner_loop() is bad here
-    mContentProcess.Init(owner_loop(), channel());
+    mContentProcess.Init(owner_loop(), GetParentProcessHandle(), channel());
 }
 
 void
 ContentProcessThread::CleanUp()
 {
     GeckoThread::CleanUp();
 }
 
--- a/dom/ipc/ContentProcessThread.h
+++ b/dom/ipc/ContentProcessThread.h
@@ -54,17 +54,17 @@ namespace dom {
 
 /**
  * ContentProcessThread is a singleton on the content process which represents
  * a background thread where tab instances live.
  */
 class ContentProcessThread : public mozilla::ipc::GeckoThread
 {
 public:
-    ContentProcessThread();
+    ContentProcessThread(ProcessHandle mParentHandle);
     ~ContentProcessThread();
 
 private:
     // Thread implementation:
     virtual void Init();
     virtual void CleanUp();
 
     ContentProcessChild mContentProcess;
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -94,16 +94,17 @@ PluginModuleChild*
 PluginModuleChild::current()
 {
     NS_ASSERTION(gInstance, "Null instance!");
     return gInstance;
 }
 
 bool
 PluginModuleChild::Init(const std::string& aPluginFilename,
+                        base::ProcessHandle aParentProcessHandle,
                         MessageLoop* aIOLoop,
                         IPC::Channel* aChannel)
 {
     _MOZ_LOG(__FUNCTION__);
 
     NS_ASSERTION(aChannel, "need a channel");
 
     if (!mObjectMap.Init()) {
@@ -129,17 +130,17 @@ PluginModuleChild::Init(const std::strin
     pluginIfile = do_QueryInterface(pluginFile);
 
     nsPluginFile lib(pluginIfile);
 
     nsresult rv = lib.LoadPlugin(mLibrary);
     NS_ASSERTION(NS_OK == rv, "trouble with mPluginFile");
     NS_ASSERTION(mLibrary, "couldn't open shared object");
 
-    if (!Open(aChannel, aIOLoop))
+    if (!Open(aChannel, aParentProcessHandle, aIOLoop))
         return false;
 
     memset((void*) &mFunctions, 0, sizeof(mFunctions));
     mFunctions.size = sizeof(mFunctions);
 
 
 #if defined(OS_LINUX)
     mShutdownFunc =
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -124,16 +124,17 @@ protected:
                                      const nsTArray<nsCString>& aValues,
                                      NPError* rv);
 
 public:
     PluginModuleChild();
     virtual ~PluginModuleChild();
 
     bool Init(const std::string& aPluginFilename,
+              base::ProcessHandle aParentProcessHandle,
               MessageLoop* aIOLoop,
               IPC::Channel* aChannel);
 
     void CleanUp();
 
     static const NPNetscapeFuncs sBrowserFuncs;
 
     static PluginModuleChild* current();
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -52,17 +52,18 @@ PR_STATIC_ASSERT(sizeof(NPIdentifier) ==
 PluginLibrary*
 PluginModuleParent::LoadModule(const char* aFilePath)
 {
     _MOZ_LOG(__FUNCTION__);
 
     // Block on the child process being launched and initialized.
     PluginModuleParent* parent = new PluginModuleParent(aFilePath);
     parent->mSubprocess.Launch();
-    parent->Open(parent->mSubprocess.GetChannel());
+    parent->Open(parent->mSubprocess.GetChannel(),
+                 parent->mSubprocess.GetChildProcessHandle());
 
     return parent;
 }
 
 
 PluginModuleParent::PluginModuleParent(const char* aFilePath) :
     mSubprocess(aFilePath)
 {
--- a/dom/plugins/PluginProcessParent.h
+++ b/dom/plugins/PluginProcessParent.h
@@ -63,29 +63,24 @@ public:
     PluginProcessParent(const std::string& aPluginFilePath);
     ~PluginProcessParent();
 
     /**
      * Asynchronously launch the plugin process.
      */
     bool Launch();
 
-    IPC::Channel* GetChannel() {
-        return channelp();
-    }
-
     virtual bool CanShutdown()
     {
         return true;
     }
 
-    base::WaitableEvent* GetShutDownEvent()
-    {
-        return GetProcessEvent();
-    }
+    using mozilla::ipc::GeckoChildProcessHost::GetShutDownEvent;
+    using mozilla::ipc::GeckoChildProcessHost::GetChannel;
+    using mozilla::ipc::GeckoChildProcessHost::GetChildProcessHandle;
 
 private:
     std::string mPluginFilePath;
 
     DISALLOW_EVIL_CONSTRUCTORS(PluginProcessParent);
 };
 
 
--- a/dom/plugins/PluginThreadChild.cpp
+++ b/dom/plugins/PluginThreadChild.cpp
@@ -46,18 +46,18 @@
 #include "chrome/common/child_process.h"
 #include "chrome/common/chrome_switches.h"
 
 using mozilla::ipc::GeckoThread;
 
 namespace mozilla {
 namespace plugins {
 
-PluginThreadChild::PluginThreadChild() :
-    GeckoThread(),
+PluginThreadChild::PluginThreadChild(ProcessHandle aParentHandle) :
+    GeckoThread(aParentHandle),
     mPlugin()
 {
 }
 
 PluginThreadChild::~PluginThreadChild()
 {
 }
 
@@ -72,17 +72,18 @@ PluginThreadChild::Init()
         CommandLine::ForCurrentProcess()->GetLooseValues();
 
     // XXX need to handle plugin args!
     DCHECK(values.size() >= 1);
 
     std::string pluginFilename = WideToUTF8(values[0]);
 
     // FIXME owner_loop() is bad here
-    mPlugin.Init(pluginFilename, owner_loop(), channel());
+    mPlugin.Init(pluginFilename,
+                 GetParentProcessHandle(), owner_loop(), channel());
 }
 
 void
 PluginThreadChild::CleanUp()
 {
     mPlugin.CleanUp();
     GeckoThread::CleanUp();
 }
--- a/dom/plugins/PluginThreadChild.h
+++ b/dom/plugins/PluginThreadChild.h
@@ -54,17 +54,17 @@
 namespace mozilla {
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 // The PluginThreadChild class represents a background thread where plugin instances
 // live.
 class PluginThreadChild : public mozilla::ipc::GeckoThread {
 public:
-    PluginThreadChild();
+    PluginThreadChild(ProcessHandle aParentHandle);
     ~PluginThreadChild();
 
 private:
     // Thread implementation:
     virtual void Init();
     virtual void CleanUp();
 
     // FIXME/cjones: this is kinda broken; this thread is generic,
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -36,16 +36,18 @@
 
 #include "GeckoChildProcessHost.h"
 
 #include "base/command_line.h"
 #include "base/path_service.h"
 #include "base/string_util.h"
 #include "chrome/common/chrome_switches.h"
 
+#include "prprf.h"
+
 #include "mozilla/ipc/GeckoThread.h"
 
 using mozilla::MonitorAutoEnter;
 using mozilla::ipc::GeckoChildProcessHost;
 
 template<>
 struct RunnableMethodTraits<GeckoChildProcessHost>
 {
@@ -54,17 +56,18 @@ struct RunnableMethodTraits<GeckoChildPr
 };
 
 GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
                                              base::WaitableEventWatcher::Delegate* aDelegate)
   : ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum
     mProcessType(aProcessType),
     mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
     mLaunched(false),
-    mDelegate(aDelegate)
+    mDelegate(aDelegate),
+    mChildProcessHandle(0)
 {
 }
 
 bool
 GeckoChildProcessHost::SyncLaunch(std::vector<std::wstring> aExtraOpts)
 {
   MessageLoop* loop = MessageLoop::current();
   MessageLoop* ioLoop = 
@@ -113,16 +116,23 @@ GeckoChildProcessHost::AsyncLaunch(std::
   cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());
 
   for (std::vector<std::wstring>::iterator it = aExtraOpts.begin();
        it != aExtraOpts.end();
        ++it) {
     cmdLine.AppendLooseValue((*it).c_str());
   }
 
+  // send the child the PID so that it can open a ProcessHandle back to us.
+  // probably don't want to do this in the long run
+  char pidstring[32];
+  PR_snprintf(pidstring, sizeof(pidstring) - 1,
+	      "%ld", base::Process::Current().pid());
+  cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
+
   cmdLine.AppendLooseValue(UTF8ToWide(XRE_ChildProcessTypeToString(mProcessType)));
 
   base::ProcessHandle process;
 #if defined(OS_WIN)
   base::LaunchApp(cmdLine, false, false, &process);
 #elif defined(OS_POSIX)
   base::LaunchApp(cmdLine.argv(), mFileMap, false, &process);
 #else
@@ -137,16 +147,20 @@ GeckoChildProcessHost::AsyncLaunch(std::
   return true;
 }
 
 void
 GeckoChildProcessHost::OnChannelConnected(int32 peer_pid)
 {
   MonitorAutoEnter mon(mMonitor);
   mLaunched = true;
+
+  if (!base::OpenProcessHandle(peer_pid, &mChildProcessHandle))
+      NS_RUNTIMEABORT("can't open handle to child process");
+
   mon.Notify();
 }
 
 // XXX/cjones: these next two methods should basically never be called.
 // after the process is launched, its channel will be used to create
 // one of our channels, AsyncChannel et al.
 void
 GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg)
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -51,16 +51,18 @@ namespace mozilla {
 namespace ipc {
 
 class GeckoChildProcessHost : public ChildProcessHost
 {
 protected:
   typedef mozilla::Monitor Monitor;
 
 public:
+  typedef base::ProcessHandle ProcessHandle;
+
   GeckoChildProcessHost(GeckoProcessType aProcessType=GeckoProcessType_Default,
                         base::WaitableEventWatcher::Delegate* aDelegate=nsnull);
 
   bool SyncLaunch(std::vector<std::wstring> aExtraOpts=std::vector<std::wstring>());
   bool AsyncLaunch(std::vector<std::wstring> aExtraOpts=std::vector<std::wstring>());
 
   virtual void OnChannelConnected(int32 peer_pid);
   virtual void OnMessageReceived(const IPC::Message& aMsg);
@@ -73,28 +75,34 @@ public:
   IPC::Channel* GetChannel() {
     return channelp();
   }
 
   base::WaitableEvent* GetShutDownEvent() {
     return GetProcessEvent();
   }
 
+  ProcessHandle GetChildProcessHandle() {
+    return mChildProcessHandle;
+  }
+
 protected:
   GeckoProcessType mProcessType;
   Monitor mMonitor;
   bool mLaunched;
   FilePath mProcessPath;
 
 #if defined(OS_POSIX)
   base::file_handle_mapping_vector mFileMap;
 #endif
 
   base::WaitableEventWatcher::Delegate* mDelegate;
 
+  ProcessHandle mChildProcessHandle;
+
 private:
   DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);
 };
 
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* __IPC_GLUE_GECKOCHILDPROCESSHOST_H__ */
--- a/ipc/glue/GeckoThread.h
+++ b/ipc/glue/GeckoThread.h
@@ -34,45 +34,56 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __IPC_GLUE_GECKOTHREAD_H__
 #define __IPC_GLUE_GECKOTHREAD_H__
 
 #include "base/thread.h"
 #include "base/lock.h"
+#include "base/process.h"
 
 #include "chrome/common/child_thread.h"
 
 #include "mozilla/ipc/ScopedXREEmbed.h"
 
 class NotificationService;
 
 namespace mozilla {
 namespace ipc {
 
 class GeckoThread : public ChildThread
 {
 public:
-  GeckoThread()
+  typedef base::ProcessHandle ProcessHandle;
+
+  GeckoThread(ProcessHandle aParentProcessHandle)
   : ChildThread(base::Thread::Options(
                     MessageLoop::TYPE_MOZILLA_CHILD, // message loop type
                     0,                               // stack size
-                    false))                          // wait for Init()?
+                    false)),                         // wait for Init()?
+    mParentProcessHandle(aParentProcessHandle)
   { }
 
 protected:
   virtual void OnControlMessageReceived(const IPC::Message& aMessage);
 
+  ProcessHandle GetParentProcessHandle() {
+    return mParentProcessHandle;
+  }
+
   // Thread implementation:
   virtual void Init();
   virtual void CleanUp();
 
   ScopedXREEmbed mXREEmbed;
 
+private:
+  ProcessHandle mParentProcessHandle;
+
   DISALLOW_EVIL_CONSTRUCTORS(GeckoThread);
 };
 
 // Copied from browser_process_impl.cc, modified slightly.
 class BrowserProcessSubThread : public base::Thread
 {
 public:
   // An enumeration of the well-known threads.
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -35,41 +35,45 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_ipc_ProtocolUtils_h
 #define mozilla_ipc_ProtocolUtils_h 1
 
+#include "base/process.h"
 #include "chrome/common/ipc_message_utils.h"
 
 #include "mozilla/ipc/RPCChannel.h"
 
 namespace mozilla {
 namespace ipc {
 
 
 // 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;
 };
 
-
 template<class ListenerT>
 class /*NS_INTERFACE_CLASS*/ IProtocolManager
 {
 public:
+    typedef base::ProcessHandle ProcessHandle;
+
     virtual int32 Register(ListenerT*) = 0;
     virtual int32 RegisterID(ListenerT*, int32) = 0;
     virtual ListenerT* Lookup(int32) = 0;
     virtual void Unregister(int32) = 0;
+    // XXX odd duck, acknowledged
+    virtual ProcessHandle OtherProcess() = 0;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 
 namespace IPC {
 
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -1021,17 +1021,21 @@ class Protocol(ipdl.ast.Protocol):
         return ExprVar('RegisterID')
 
     def lookupIDMethod(self):
         return ExprVar('Lookup')
 
     def unregisterMethod(self):
         return ExprVar('Unregister')
 
+    def otherProcessMethod(self):
+        return ExprVar('OtherProcess')
+
     def nextActorIdExpr(self, side):
+        assert self.decl.type.isToplevel()
         if side is 'parent':   op = '++'
         elif side is 'child':  op = '--'
         return ExprPrefixUnop(self.lastActorIdVar(), op)
 
     # an actor's C++ private variables
     def lastActorIdVar(self):
         assert self.decl.type.isToplevel()
         return ExprVar('mLastRouteId')
@@ -1056,16 +1060,20 @@ class Protocol(ipdl.ast.Protocol):
     def idVar(self):
         assert not self.decl.type.isToplevel()
         return ExprVar('mId')
 
     def managerVar(self):
         assert not self.decl.type.isToplevel()
         return ExprVar('mManager')
 
+    def otherProcessVar(self):
+        assert self.decl.type.isToplevel()
+        return ExprVar('mOtherProcess')
+
     @staticmethod
     def upgrade(protocol):
         assert isinstance(protocol, ipdl.ast.Protocol)
         protocol.__class__ = Protocol
         return protocol
 
 ##-----------------------------------------------------------------------------
 
@@ -2147,16 +2155,17 @@ class _GenerateProtocolActorHeader(ipdl.
 
         self.cls.addstmt(Whitespace.NL)
 
         self.cls.addstmts([
             Label.PRIVATE,
             Typedef(Type('IPC::Message'), 'Message'),
             Typedef(Type(p.channelName()), 'Channel'),
             Typedef(Type(p.fqListenerName()), 'ChannelListener'),
+            Typedef(Type('base::ProcessHandle'), 'ProcessHandle'),
             Whitespace.NL,
         ])
 
         self.cls.addstmt(Label.PUBLIC)
         # Actor()
         ctor = ConstructorDefn(ConstructorDecl(self.clsname))
         if p.decl.type.isToplevel():
             ctor.memberinits = [
@@ -2179,29 +2188,35 @@ class _GenerateProtocolActorHeader(ipdl.
         dtor.addstmt(StmtExpr(ExprCall(ExprVar('MOZ_COUNT_DTOR'),
                                                [ ExprVar(self.clsname) ])))
         self.cls.addstmts([ dtor, Whitespace.NL ])
 
         if p.decl.type.isToplevel():
             # Open()
             aTransportVar = ExprVar('aTransport')
             aThreadVar = ExprVar('aThread')
+            processvar = ExprVar('aOtherProcess')
             openmeth = MethodDefn(
                 MethodDecl(
                     'Open',
                     params=[ Decl(Type('Channel::Transport', ptr=True),
                                       aTransportVar.name),
+                             Decl(Type('ProcessHandle'), processvar.name),
                              Decl(Type('MessageLoop', ptr=True),
                                       aThreadVar.name +' = 0') ],
                     ret=Type.BOOL))
 
-            openmeth.addstmt(StmtReturn(
-                ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
-                         [ aTransportVar, aThreadVar ])))
-            self.cls.addstmts([ openmeth, Whitespace.NL ])
+            openmeth.addstmts([
+                StmtExpr(ExprAssn(p.otherProcessVar(), processvar)),
+                StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
+                                    [ aTransportVar, aThreadVar ]))
+            ])
+            self.cls.addstmts([
+                openmeth,
+                Whitespace.NL ])
 
             # Close()
             closemeth = MethodDefn(MethodDecl('Close'))
             closemeth.addstmt(StmtExpr(
                 ExprCall(ExprSelect(p.channelVar(), '.', 'Close'))))
             self.cls.addstmts([ closemeth, Whitespace.NL ])
 
         ## OnMessageReceived()/OnCallReceived()
@@ -2301,17 +2316,19 @@ class _GenerateProtocolActorHeader(ipdl.
 
         # private: members and methods
         self.cls.addstmts([ Label.PRIVATE,
                             StmtDecl(Decl(p.channelType(), 'mChannel')) ])
         if p.decl.type.isToplevel():
             self.cls.addstmts([
                 StmtDecl(Decl(Type('IDMap', T=Type('ChannelListener')),
                               p.actorMapVar().name)),
-                StmtDecl(Decl(_actorIdType(), p.lastActorIdVar().name))
+                StmtDecl(Decl(_actorIdType(), p.lastActorIdVar().name)),
+                StmtDecl(Decl(Type('ProcessHandle'),
+                              p.otherProcessVar().name))
             ])
         elif p.decl.type.isManaged():
             self.cls.addstmts([
                 StmtDecl(Decl(_actorIdType(), p.idVar().name)),
                 StmtDecl(Decl(p.managerCxxType(ptr=1), p.managerVar().name))
             ])
 
         self.ns.addstmts([ self.cls, Whitespace.NL, Whitespace.NL ])
@@ -2345,16 +2362,20 @@ class _GenerateProtocolActorHeader(ipdl.
         lookup = MethodDefn(MethodDecl(
             p.lookupIDMethod().name,
             params=[ Decl(_actorIdType(), idvar.name) ],
             ret=listenertype, virtual=1))
         unregister = MethodDefn(MethodDecl(
             p.unregisterMethod().name,
             params=[ Decl(_actorIdType(), idvar.name) ],
             virtual=1))
+        otherprocess = MethodDefn(MethodDecl(
+            p.otherProcessMethod().name,
+            ret=Type('ProcessHandle'),
+            virtual=1))
 
         if p.decl.type.isToplevel():
             tmpvar = ExprVar('tmp')
             register.addstmts([
                 StmtDecl(Decl(_actorIdType(), tmpvar.name),
                          p.nextActorIdExpr(self.side)),
                 StmtExpr(ExprCall(
                     ExprSelect(p.actorMapVar(), '.', 'AddWithID'),
@@ -2368,32 +2389,42 @@ class _GenerateProtocolActorHeader(ipdl.
                 StmtReturn(idvar)
             ])
             lookup.addstmt(StmtReturn(
                 ExprCall(ExprSelect(p.actorMapVar(), '.', 'Lookup'),
                          [ idvar ])))
             unregister.addstmt(StmtReturn(
                 ExprCall(ExprSelect(p.actorMapVar(), '.', 'Remove'),
                          [ idvar ])))
+            otherprocess.addstmt(StmtReturn(p.otherProcessVar()))
         # delegate registration to manager
         else:
             register.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->', p.registerMethod().name),
                 [ routedvar ])))
             registerid.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->', p.registerIDMethod().name),
                 [ routedvar, idvar ])))
             lookup.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->', p.lookupIDMethod().name),
                 [ idvar ])))
             unregister.addstmt(StmtReturn(ExprCall(
                 ExprSelect(p.managerVar(), '->', p.unregisterMethod().name),
                 [ idvar ])))
-
-        return [ register, registerid, lookup, unregister, Whitespace.NL ]
+            otherprocess.addstmt(StmtReturn(ExprCall(
+                ExprSelect(p.managerVar(), '->',
+                           p.otherProcessMethod().name))))
+
+        return [ register,
+                 registerid,
+                 lookup,
+                 unregister,
+                 otherprocess,
+                 Whitespace.NL ]
+
 
 
     ##-------------------------------------------------------------------------
     ## The next few functions are the crux of the IPDL code generator.
     ## They generate code for all the nasty work of message
     ## serialization/deserialization and dispatching handlers for
     ## received messages.
     ##
--- a/ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.cpp
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.cpp
@@ -40,30 +40,30 @@
 
 #include "IPDLUnitTests.h"
 
 using mozilla::ipc::GeckoThread;
 
 namespace mozilla {
 namespace _ipdltest {
 
-IPDLUnitTestThreadChild::IPDLUnitTestThreadChild() :
-    GeckoThread()
+IPDLUnitTestThreadChild::IPDLUnitTestThreadChild(ProcessHandle aParentHandle) :
+    GeckoThread(aParentHandle)
 {
 }
 
 IPDLUnitTestThreadChild::~IPDLUnitTestThreadChild()
 {
 }
 
 void
 IPDLUnitTestThreadChild::Init()
 {
     GeckoThread::Init();
-    IPDLUnitTestChildInit(channel(), owner_loop());
+    IPDLUnitTestChildInit(channel(), GetParentProcessHandle(), owner_loop());
 }
 
 void
 IPDLUnitTestThreadChild::CleanUp()
 {
     GeckoThread::CleanUp();
     IPDLUnitTestChildCleanUp();
 }
--- a/ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.h
+++ b/ipc/ipdl/test/cxx/IPDLUnitTestThreadChild.h
@@ -42,17 +42,17 @@
 #include "mozilla/ipc/GeckoThread.h"
 
 namespace mozilla {
 namespace _ipdltest {
 
 class IPDLUnitTestThreadChild : public mozilla::ipc::GeckoThread
 {
 public:
-  IPDLUnitTestThreadChild();
+  IPDLUnitTestThreadChild(ProcessHandle aParentHandle);
   ~IPDLUnitTestThreadChild();
 
 protected:
   virtual void Init();
   virtual void CleanUp();
 };
 
 } // namespace _ipdltest
--- a/ipc/ipdl/test/cxx/IPDLUnitTests.h
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.h
@@ -35,16 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla__ipdltest_IPDLUnitTests_h
 #define mozilla__ipdltest_IPDLUnitTests_h 1
 
 #include "base/message_loop.h"
+#include "base/process.h"
 #include "chrome/common/ipc_channel.h"
 
 
 #define MOZ_IPDL_TESTFAIL_LABEL "TEST-UNEXPECTED-FAIL"
 #define MOZ_IPDL_TESTPASS_LABEL "TEST-PASS"
 
 // NB: these are named like the similar functions in
 // xpcom/test/TestHarness.h.  The names should nominally be kept in
@@ -77,17 +78,19 @@ const char* const IPDLUnitTestName();
 void IPDLUnitTestMain(void* aData);
 
 // TODO: could clean up parent actor/subprocess
 
 
 //-----------------------------------------------------------------------------
 // child process only
 
-void IPDLUnitTestChildInit(IPC::Channel* transport, MessageLoop* worker);
+void IPDLUnitTestChildInit(IPC::Channel* transport,
+                           base::ProcessHandle parent,
+                           MessageLoop* worker);
 void IPDLUnitTestChildCleanUp();
 
 
 } // namespace _ipdltest
 } // namespace mozilla
 
 
 #endif // ifndef mozilla__ipdltest_IPDLUnitTests_h
--- a/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
+++ b/ipc/ipdl/test/cxx/IPDLUnitTests.template.cpp
@@ -136,16 +136,18 @@ IPDLUnitTestMain(void* aData)
     IPDLUnitTestSubprocess* subprocess = new IPDLUnitTestSubprocess();
     if (!subprocess->SyncLaunch(testCaseArgs))
         fail("problem launching subprocess");
 
     IPC::Channel* transport = subprocess->GetChannel();
     if (!transport)
         fail("no transport");
 
+    base::ProcessHandle child = subprocess->GetChildProcessHandle();
+
     switch (test) {
 //-----------------------------------------------------------------------------
 //===== TEMPLATED =====
 ${PARENT_MAIN_CASES}
 //-----------------------------------------------------------------------------
 
     default:
         fail("not reached");
@@ -164,17 +166,19 @@ namespace {
 void* gChildActor = NULL;
 }
 
 
 namespace mozilla {
 namespace _ipdltest {
 
 void
-IPDLUnitTestChildInit(IPC::Channel* transport, MessageLoop* worker)
+IPDLUnitTestChildInit(IPC::Channel* transport,
+                      base::ProcessHandle parent,
+                      MessageLoop* worker)
 {
     switch (IPDLUnitTest()) {
 //-----------------------------------------------------------------------------
 //===== TEMPLATED =====
 ${CHILD_INIT_CASES}
 //-----------------------------------------------------------------------------
 
     default:
--- a/ipc/ipdl/test/cxx/genIPDLUnitTests.py
+++ b/ipc/ipdl/test/cxx/genIPDLUnitTests.py
@@ -59,28 +59,28 @@ def main(argv):
         return "%s";'''%(t, t) for t in unittests ])
 
 
     parent_main_cases = '\n'.join([
 '''    case %s: {
         %sParent** parent =
         reinterpret_cast<%sParent**>(&gParentActor);
         *parent = new %sParent();
-        (*parent)->Open(transport);
+        (*parent)->Open(transport, child);
         return (*parent)->Main();
         }
 '''% (t, t, t, t) for t in unittests ])
 
 
     child_init_cases = '\n'.join([
 '''    case %s: {
         %sChild** child =
             reinterpret_cast<%sChild**>(&gChildActor);
         *child = new %sChild();
-        (*child)->Open(transport, worker);
+        (*child)->Open(transport, parent, worker);
         return;
     }
 '''% (t, t, t, t) for t in unittests ])
 
     child_cleanup_cases = '\n'.join([
 '''    case %s: {
         %sChild** child =
             reinterpret_cast<%sChild**>(&gChildActor);
--- a/ipc/test-harness/TestThreadChild.cpp
+++ b/ipc/test-harness/TestThreadChild.cpp
@@ -35,30 +35,30 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "TestThreadChild.h"
 
 using mozilla::test::TestThreadChild;
 using mozilla::ipc::GeckoThread;
 
-TestThreadChild::TestThreadChild() :
-    GeckoThread()
+TestThreadChild::TestThreadChild(ProcessHandle aParentHandle) :
+    GeckoThread(aParentHandle)
 {
 }
 
 TestThreadChild::~TestThreadChild()
 {
 }
 
 void
 TestThreadChild::Init()
 {
     GeckoThread::Init();
-    mChild.Open(channel(), owner_loop());
+    mChild.Open(channel(), GetParentProcessHandle(), owner_loop());
 }
 
 void
 TestThreadChild::CleanUp()
 {
     GeckoThread::CleanUp();
     mChild.Close();
 }
--- a/ipc/test-harness/TestThreadChild.h
+++ b/ipc/test-harness/TestThreadChild.h
@@ -42,17 +42,17 @@
 #include "mozilla/test/TestChild.h"
 
 namespace mozilla {
 namespace test {
 
 class TestThreadChild : public mozilla::ipc::GeckoThread
 {
 public:
-    TestThreadChild();
+    TestThreadChild(ProcessHandle aParentHandle);
     ~TestThreadChild();
 
 protected:
     virtual void Init();
     virtual void CleanUp();
 
 private:
     TestChild mChild;
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -65,16 +65,17 @@
 #include "nsWidgetsCID.h"
 #include "nsXPFEComponentsCID.h"
 #include "nsXREDirProvider.h"
 
 #ifdef MOZ_IPC
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/message_loop.h"
+#include "base/process_util.h"
 #include "chrome/common/child_process.h"
 
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/GeckoThread.h"
 #include "ScopedXREEmbed.h"
 
 #include "mozilla/plugins/PluginThreadChild.h"
 #include "mozilla/dom/ContentProcessThread.h"
@@ -253,60 +254,74 @@ static GeckoProcessType sChildProcessTyp
 
 static MessageLoop* sIOMessageLoop;
 
 nsresult
 XRE_InitChildProcess(int aArgc,
                      char* aArgv[],
                      GeckoProcessType aProcess)
 {
-  NS_ENSURE_ARG_MIN(aArgc, 1);
+  NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
 
 #if defined(MOZ_WIDGET_GTK2)
   g_thread_init(NULL);
 #endif
 
   if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) {
 #ifdef OS_POSIX
       printf("\n\nCHILDCHILDCHILDCHILD\n  debug me @%d\n\n", getpid());
       sleep(30);
 #elif defined(OS_WIN)
       Sleep(30000);
 #endif
   }
 
+  // child processes launched by GeckoChildProcessHost get this magic
+  // argument appended to their command lines
+  const char* const parentPIDString = aArgv[aArgc-1];
+  NS_ABORT_IF_FALSE(parentPIDString, "NULL parent PID");
+  --aArgc;
+
+  char* end = 0;
+  base::ProcessId parentPID = strtol(parentPIDString, &end, 10);
+  NS_ABORT_IF_FALSE(!*end, "invalid parent PID");
+  base::ProcessHandle parentHandle;
+  NS_ABORT_IF_FALSE(
+      base::OpenProcessHandle(parentPID, &parentHandle),
+      "can't open handle to parent");
+
   base::AtExitManager exitManager;
   CommandLine::Init(aArgc, aArgv);
   MessageLoopForIO mainMessageLoop;
 
   {
     ChildThread* mainThread;
 
     switch (aProcess) {
     case GeckoProcessType_Default:
-      mainThread = new GeckoThread();
+      mainThread = new GeckoThread(parentHandle);
       break;
 
     case GeckoProcessType_Plugin:
-      mainThread = new PluginThreadChild();
+      mainThread = new PluginThreadChild(parentHandle);
       break;
 
     case GeckoProcessType_Content:
-      mainThread = new ContentProcessThread();
+      mainThread = new ContentProcessThread(parentHandle);
       break;
 
     case GeckoProcessType_TestHarness:
-      mainThread = new TestThreadChild();
+      mainThread = new TestThreadChild(parentHandle);
       break;
 
     case GeckoProcessType_IPDLUnitTest:
 #ifdef MOZ_IPDL_TESTS
-      mainThread = new IPDLUnitTestThreadChild();
+      mainThread = new IPDLUnitTestThreadChild(parentHandle);
 #else
       NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests");
 #endif
       break;
 
     default:
       NS_RUNTIMEABORT("Unknown main thread class");
     }
@@ -409,17 +424,18 @@ XRE_InitParentProcess(int aArgc,
 static void
 IPCTestHarnessMain(void* data)
 {
     TestProcessParent* subprocess = new TestProcessParent(); // leaks
     bool launched = subprocess->SyncLaunch();
     NS_ASSERTION(launched, "can't launch subprocess");
 
     TestParent* parent = new TestParent(); // leaks
-    parent->Open(subprocess->GetChannel());
+    parent->Open(subprocess->GetChannel(),
+                 subprocess->GetChildProcessHandle());
     parent->DoStuff();
 }
 
 int
 XRE_RunIPCTestHarness(int aArgc, char* aArgv[])
 {
     nsresult rv =
         XRE_InitParentProcess(