add IPDL test harness and the mountains of code that entails. add |bool| type to IPDL. fix bugs in nascent async/sync channels.
authorChris Jones <jones.chris.g@gmail.com>
Tue, 14 Jul 2009 00:12:50 -0500
changeset 35779 1769c830e612ef844c2b7df27028f570e34af760
parent 35778 2e27ae79e54420a28a406df7abbf3c9e6073ebc4
child 35780 3ed7ec034b950b04a039da01e2b8967be9a108d1
push id10694
push userbsmedberg@mozilla.com
push dateMon, 14 Dec 2009 15:23:10 +0000
treeherdermozilla-central@683dfdc4adf0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.2a1pre
add IPDL test harness and the mountains of code that entails. add |bool| type to IPDL. fix bugs in nascent async/sync channels.
dom/ipc/Makefile.in
ipc/Makefile.in
ipc/glue/AsyncChannel.cpp
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/GeckoChildProcessHost.h
ipc/glue/RPCChannel.cpp
ipc/glue/SyncChannel.cpp
ipc/ipdl/Makefile.in
ipc/ipdl/ipdl/builtin.py
ipc/ipdl/ipdl/type.py
ipc/test-harness/Makefile.in
ipc/test-harness/Test.ipdl
ipc/test-harness/TestChild.cpp
ipc/test-harness/TestChild.h
ipc/test-harness/TestParent.cpp
ipc/test-harness/TestParent.h
ipc/test-harness/TestProcessParent.cpp
ipc/test-harness/TestProcessParent.h
ipc/test-harness/TestThreadChild.cpp
ipc/test-harness/TestThreadChild.h
ipc/test-harness/app/Makefile.in
ipc/test-harness/app/TestApp.cpp
ipc/test-harness/ipdl.mk
toolkit/library/libxul-config.mk
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/nsXULAppAPI.h
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -1,8 +1,44 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Firefox.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation <http://www.mozilla.org/>.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# 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 *****
+
 DEPTH = ../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = dom
--- a/ipc/Makefile.in
+++ b/ipc/Makefile.in
@@ -36,12 +36,12 @@
 
 DEPTH = ..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-DIRS += ipdl chromium glue testshell
+DIRS += ipdl chromium glue test-harness testshell
 TOOL_DIRS = app
 
 include $(topsrcdir)/config/rules.mk
--- a/ipc/glue/AsyncChannel.cpp
+++ b/ipc/glue/AsyncChannel.cpp
@@ -59,22 +59,27 @@ AsyncChannel::Open(Transport* aTransport
     NS_PRECONDITION(!mTransport, "Open() called > once");
     NS_PRECONDITION(aTransport, "need transport layer");
 
     // FIXME need to check for valid channel
 
     mTransport = aTransport;
     mTransport->set_listener(this);
 
-    // FIXME do away with this
+    // FIXME figure out whether we're in parent or child, grab IO loop
+    // appropriately
     bool needOpen = true;
     if(!aIOLoop) {
+        // parent
         needOpen = false;
         aIOLoop = BrowserProcessSubThread
                   ::GetMessageLoop(BrowserProcessSubThread::IO);
+        // FIXME assuming that the parent waits for the OnConnected event.
+        // FIXME see GeckoChildProcessHost.cpp.  bad assumption!
+        mChannelState = ChannelIdle;
     }
 
     mIOLoop = aIOLoop;
     mWorkerLoop = MessageLoop::current();
 
     NS_ASSERTION(mIOLoop, "need an IO loop");
     NS_ASSERTION(mWorkerLoop, "need a worker loop");
 
@@ -93,16 +98,20 @@ AsyncChannel::Close()
     // FIXME impl
 
     mChannelState = ChannelClosed;
 }
 
 bool
 AsyncChannel::Send(Message* msg)
 {
+    NS_ASSERTION(ChannelIdle == mChannelState
+                 || ChannelWaiting == mChannelState,
+                 "trying to Send() to a channel not yet open");
+
     NS_PRECONDITION(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
     mIOLoop->PostTask(FROM_HERE,
                       NewRunnableMethod(this, &AsyncChannel::OnSend, msg));
     return true;
 }
 
 void
 AsyncChannel::OnDispatchMessage(const Message& msg)
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -90,20 +90,36 @@ GeckoChildProcessHost::Launch(std::vecto
 #error Bad!
 #endif
 
   if (!process) {
     return false;
   }
   SetHandle(process);
 
+  // FIXME/cjones: should have the option for sync/async launch.
+  // however, since most clients already expect this launch to be 
+  // synchronous wrt the channel connecting, we'll hack a bit here.
+  // (at least we're on the IO thread and not blocking main ...)
+  MessageLoop* loop = MessageLoop::current();
+  bool old_state = loop->NestableTasksAllowed();
+  loop->SetNestableTasksAllowed(true);
+  // spin the loop until OnChannelConnected() comes in, which will Quit() us
+  loop->Run();
+  loop->SetNestableTasksAllowed(old_state);
+
   return true;
 }
 
 void
+GeckoChildProcessHost::OnChannelConnected(int32 peer_pid)
+{
+    MessageLoop::current()->Quit();
+}
+void
 GeckoChildProcessHost::OnMessageReceived(const IPC::Message& aMsg)
 {
 }
 
 void
 GeckoChildProcessHost::OnChannelError()
 {
   // XXXbent Notify that the child process is gone?
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -51,25 +51,30 @@ namespace ipc {
 class GeckoChildProcessHost : public ChildProcessHost
 {
 public:
   GeckoChildProcessHost(GeckoChildProcessType aProcessType=GeckoChildProcess_Default);
 
   bool Launch(std::vector<std::wstring> aExtraOpts=std::vector<std::wstring>());
 
   // FIXME/cjones: these should probably disappear
+  virtual void OnChannelConnected(int32 peer_pid);
   virtual void OnMessageReceived(const IPC::Message& aMsg);
   virtual void OnChannelError();
 
   virtual bool CanShutdown() { return true; }
 
   IPC::Channel* GetChannel() {
     return channelp();
   }
 
+  base::WaitableEvent* GetShutDownEvent() {
+    return GetProcessEvent();
+  }
+
 protected:
   GeckoChildProcessType mProcessType;
   FilePath mProcessPath;
 
 #if defined(OS_POSIX)
   base::file_handle_mapping_vector mFileMap;
 #endif
 
--- a/ipc/glue/RPCChannel.cpp
+++ b/ipc/glue/RPCChannel.cpp
@@ -53,16 +53,20 @@ struct RunnableMethodTraits<mozilla::ipc
 
 namespace mozilla {
 namespace ipc {
 
 
 bool
 RPCChannel::Call(Message* msg, Message* reply)
 {
+    NS_ASSERTION(ChannelIdle == mChannelState
+                 || ChannelWaiting == mChannelState,
+                 "trying to Send() to a channel not yet open");
+
     NS_PRECONDITION(msg->is_rpc(), "can only Call() RPC messages here");
 
     mMutex.Lock();
 
     mChannelState = ChannelWaiting;
 
     mPending.push(*msg);
     AsyncChannel::Send(msg);
--- a/ipc/glue/SyncChannel.cpp
+++ b/ipc/glue/SyncChannel.cpp
@@ -52,16 +52,20 @@ struct RunnableMethodTraits<mozilla::ipc
 };
 
 namespace mozilla {
 namespace ipc {
 
 bool
 SyncChannel::Send(Message* msg, Message* reply)
 {
+    NS_ASSERTION(ChannelIdle == mChannelState
+                 || ChannelWaiting == mChannelState,
+                 "trying to Send() to a channel not yet open");
+
     NS_PRECONDITION(msg->is_sync(), "can only Send() sync messages here");
 
     MutexAutoLock lock(mMutex);
 
     mChannelState = ChannelWaiting;
     mPendingReply = msg->type() + 1;
     /*assert*/AsyncChannel::Send(msg);
 
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -40,16 +40,17 @@ srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 IPDLDIRS = \
   dom/plugins \
   dom/ipc \
+  ipc/test-harness \
   ipc/testshell \
   $(NULL)
 
 vpath %.ipdl $(topsrcdir)
 
 define ADD_IPDLDIR
 include $(topsrcdir)/$(IPDLDIR)/ipdl.mk
 ALL_IPDLSRCS += $$(IPDLSRCS:%=$(IPDLDIR)/%)
--- a/ipc/ipdl/ipdl/builtin.py
+++ b/ipc/ipdl/ipdl/builtin.py
@@ -31,16 +31,17 @@
 # ***** END LICENSE BLOCK *****
 
 # WARNING: the syntax of the builtin types is not checked, so please
 # don't add something syntactically invalid.  It will not be fun to
 # track down the bug.
 
 Types = (
     # C types
+    'bool',
     'char',
     'short',
     'int',
     'long',
     'float',
     'double',
 
     # stdint types
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -472,20 +472,20 @@ class GatherDecls(Visitor):
                         "tag `%s' is supposed to be of state type, but is instead of type `%s'",
                         statename,
                         statedecl.type.typename()))
             else:
                 param.type.state = statedecl
 
         for msg in p.messageDecls:
             for iparam in msg.inParams:
-                if iparam.type.isIPDL() and iparam.type.isActor():
+                if iparam.type and iparam.type.isIPDL() and iparam.type.isActor():
                     resolvestate(iparam)
             for oparam in msg.outParams:
-                if oparam.type.isIPDL() and oparam.type.isActor():
+                if oparam.type and oparam.type.isIPDL() and oparam.type.isActor():
                     resolvestate(oparam)
 
         # FIXME/cjones declare all the little C++ thingies that will
         # be generated.  they're not relevant to IPDL itself, but
         # those ("invisible") symbols can clash with others in the
         # IPDL spec, and we'd like to catch those before C++ compilers
         # are allowed to obfuscate the error
 
@@ -623,21 +623,25 @@ class GatherDecls(Visitor):
                 self.symtab.declare(pdecl)
                 return pdecl
 
         for i, inparam in enumerate(md.inParams):
             pdecl = paramToDecl(inparam)
             if pdecl is not None:
                 msgtype.params.append(pdecl.type)
                 md.inParams[i] = pdecl
+            else:
+                md.inParams[i].type = None
         for i, outparam in enumerate(md.outParams):
             pdecl = paramToDecl(outparam)
             if pdecl is not None:
                 msgtype.returns.append(pdecl.type)
                 md.outParams[i] = pdecl
+            else:
+                md.outParams[i].type = None
 
         self.symtab.exitScope(md)
 
         decl = Decl(loc)
         decl.progname = msgname
         decl.type = msgtype
 
         self.symtab.declare(decl)
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/Makefile.in
@@ -0,0 +1,70 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Firefox.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation <http://www.mozilla.org/>.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# 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 *****
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipctestharness
+
+EXPORTS_NAMESPACES = mozilla/test
+EXPORTS_mozilla/test =				\
+	TestChild.h				\
+	TestParent.h				\
+	TestProcessParent.h			\
+	TestThreadChild.h			\
+	$(NULL)
+
+LIBRARY_NAME = ipctestharness_s
+LIBXUL_LIBRARY = 1
+FORCE_STATIC_LIB = 1
+EXPORT_LIBRARY = 1
+
+CPPSRCS =					\
+  TestChild.cpp					\
+  TestParent.cpp				\
+  TestProcessParent.cpp				\
+  TestThreadChild.cpp				\
+  $(NULL)
+
+TOOL_DIRS += app
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/Test.ipdl
@@ -0,0 +1,40 @@
+namespace mozilla {
+namespace test {
+
+sync protocol Test
+{
+both:
+    sync Ping() returns (int status);
+
+parent:
+    GetValue(String key);
+    GetValues(StringArray keys);
+    sync SetValue(String key, String val) returns (bool ok);
+
+child:
+    TellValue(String key, String val);
+    TellValues(StringArray keys, StringArray vals);
+
+state START:
+    recv Ping goto START;
+    send Ping goto START;
+
+    recv SetValue goto HAVE_VALUES;
+
+state HAVE_VALUES:
+    recv Ping goto HAVE_VALUES;
+    send Ping goto HAVE_VALUES;
+
+    recv SetValue goto HAVE_VALUES;
+
+    recv GetValue goto TELLING_VALUE;
+    recv GetValues goto TELLING_VALUES;
+
+state TELLING_VALUE:
+    send TellValue goto HAVE_VALUES;
+state TELLING_VALUES:
+    send TellValues goto HAVE_VALUES;
+};
+
+} // namespace test
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestChild.cpp
@@ -0,0 +1,32 @@
+#include "mozilla/test/TestChild.h"
+
+using mozilla::test::TestChild;
+
+// C++ file contents
+nsresult TestChild::RecvPing(int* status)
+{
+    *status = 42;
+    return NS_OK;
+}
+
+nsresult TestChild::RecvTellValue(
+            const String& key,
+            const String& val)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult TestChild::RecvTellValues(
+            const StringArray& keys,
+            const StringArray& vals)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+TestChild::TestChild()
+{
+}
+
+TestChild::~TestChild()
+{
+}
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestChild.h
@@ -0,0 +1,25 @@
+#include "mozilla/test/TestProtocolChild.h"
+
+namespace mozilla {
+namespace test {
+
+// Header file contents
+class TestChild :
+    public TestProtocolChild
+{
+protected:
+    virtual nsresult RecvPing(int* status);
+    virtual nsresult RecvTellValue(
+                const String& key,
+                const String& val);
+    virtual nsresult RecvTellValues(
+                const StringArray& keys,
+                const StringArray& vals);
+
+public:
+    TestChild();
+    virtual ~TestChild();
+};
+
+} // namespace test
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestParent.cpp
@@ -0,0 +1,44 @@
+#include "mozilla/test/TestParent.h"
+
+using mozilla::test::TestParent;
+
+void
+TestParent::DoStuff()
+{
+    int ping;
+    SendPing(&ping);
+
+    printf("[TestParent] child replied to ping with status code %d\n", ping);
+}
+
+// C++ file contents
+nsresult TestParent::RecvPing(int* status)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult TestParent::RecvGetValue(const String& key)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult TestParent::RecvGetValues(const StringArray& keys)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult TestParent::RecvSetValue(
+            const String& key,
+            const String& val,
+            bool* ok)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+TestParent::TestParent()
+{
+}
+
+TestParent::~TestParent()
+{
+}
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestParent.h
@@ -0,0 +1,26 @@
+#include "mozilla/test/TestProtocolParent.h"
+
+namespace mozilla {
+namespace test {
+
+// Header file contents
+class TestParent :
+    public TestProtocolParent
+{
+    virtual nsresult RecvPing(int* status);
+    virtual nsresult RecvGetValue(const String& key);
+    virtual nsresult RecvGetValues(const StringArray& keys);
+    virtual nsresult RecvSetValue(
+                const String& key,
+                const String& val,
+                bool* ok);
+
+public:
+    TestParent();
+    virtual ~TestParent();
+
+    void DoStuff();
+};
+
+} // namespace test
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestProcessParent.cpp
@@ -0,0 +1,19 @@
+#include "mozilla/test/TestProcessParent.h"
+using mozilla::ipc::GeckoChildProcessHost;
+
+namespace mozilla {
+namespace test {
+
+
+TestProcessParent::TestProcessParent() :
+    GeckoChildProcessHost(GeckoChildProcess_TestHarness)
+{
+}
+
+TestProcessParent::~TestProcessParent()
+{
+}
+
+
+} // namespace test
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestProcessParent.h
@@ -0,0 +1,30 @@
+#ifndef mozilla_test_TestProcessParent_h
+#define mozilla_test_TestProcessParent_h 1
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+namespace mozilla {
+namespace test {
+//-----------------------------------------------------------------------------
+
+class TestProcessParent : public mozilla::ipc::GeckoChildProcessHost
+{
+public:
+    TestProcessParent();
+    ~TestProcessParent();
+
+    /**
+     * Asynchronously launch the plugin process.
+     */
+    // Could override parent Launch, but don't need to here
+    //bool Launch();
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(TestProcessParent);
+};
+
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // ifndef mozilla_test_TestProcessParent_h
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestThreadChild.cpp
@@ -0,0 +1,27 @@
+#include "mozilla/test/TestThreadChild.h"
+using mozilla::test::TestThreadChild;
+
+using mozilla::ipc::GeckoThread;
+
+TestThreadChild::TestThreadChild() :
+    GeckoThread()
+{
+}
+
+TestThreadChild::~TestThreadChild()
+{
+}
+
+void
+TestThreadChild::Init()
+{
+    GeckoThread::Init();
+    mChild.Open(channel(), owner_loop());
+}
+
+void
+TestThreadChild::CleanUp()
+{
+    GeckoThread::CleanUp();
+    mChild.Close();
+}
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/TestThreadChild.h
@@ -0,0 +1,27 @@
+#ifndef mozilla_test_TestThreadChild_h
+#define mozilla_test_TestThreadChild_h
+
+#include "mozilla/ipc/GeckoThread.h"
+#include "mozilla/test/TestChild.h"
+
+namespace mozilla {
+namespace test {
+
+class TestThreadChild : public mozilla::ipc::GeckoThread
+{
+public:
+    TestThreadChild();
+    ~TestThreadChild();
+
+protected:
+    virtual void Init();
+    virtual void CleanUp();
+
+private:
+    TestChild mChild;
+};
+
+} // namespace test
+} // namespace mozilla
+
+#endif // ifndef mozilla_test_TestThreadChild_h
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/app/Makefile.in
@@ -0,0 +1,74 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla IPCShell.
+#
+# The Initial Developer of the Original Code is
+#   Ben Turner <bent.mozilla@gmail.com>.
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# 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 *****
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipctestharness
+PROGRAM = $(MODULE)$(BIN_SUFFIX)
+ENABLE_CXX_EXCEPTIONS = 1
+
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+
+CPPSRCS = \
+  TestApp.cpp \
+  $(NULL)
+
+LIBS = \
+  $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
+  $(LIBXUL_LIBS) \
+  $(MOZ_JS_LIBS) \
+  $(NSPR_LIBS) \
+  $(NULL)
+
+NSDISTMODE = copy
+
+include $(topsrcdir)/config/config.mk
+
+ifdef _MSC_VER
+ifdef WINCE
+WIN32_EXE_LDFLAGS += -ENTRY:mainWCRTStartup
+else
+WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
+endif
+endif
+
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/app/TestApp.cpp
@@ -0,0 +1,48 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla IPCShell.
+ *
+ * The Initial Developer of the Original Code is
+ *   Ben Turner <bent.mozilla@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * 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 ***** */
+
+#include "nsXULAppAPI.h"
+
+#if defined(XP_WIN)
+#include <windows.h>
+#include "nsWindowsWMain.cpp"
+#endif
+
+int
+main(int argc, char* argv[])
+{
+    return XRE_RunIPCTestHarness(argc, argv);
+}
new file mode 100644
--- /dev/null
+++ b/ipc/test-harness/ipdl.mk
@@ -0,0 +1,3 @@
+IPDLSRCS = \
+  Test.ipdl \
+  $(NULL)
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -93,16 +93,17 @@ endif
 # dependent libraries
 ifdef MOZ_IPC
 STATIC_LIBS += \
   domipc_s \
   domplugins_s \
   mozipc_s \
   chromium_s \
   ipcshell_s \
+  ipctestharness_s \
   $(NULL)
 
 ifeq (Linux,$(OS_ARCH))
 OS_LIBS += -lrt
 endif
 ifeq (WINNT,$(OS_ARCH))
 OS_LIBS += psapi.lib
 endif
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -69,29 +69,36 @@
 #include "ScopedXREEmbed.h"
 
 #include "mozilla/plugins/PluginThreadChild.h"
 #include "TabThread.h"
 
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/TestShellThread.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
+#include "mozilla/test/TestParent.h"
+#include "mozilla/test/TestProcessParent.h"
+#include "mozilla/test/TestThreadChild.h"
 #include "mozilla/Monitor.h"
 
 using mozilla::ipc::BrowserProcessSubThread;
 using mozilla::ipc::GeckoChildProcessHost;
 using mozilla::ipc::GeckoThread;
 using mozilla::ipc::ScopedXREEmbed;
 
 using mozilla::plugins::PluginThreadChild;
 using mozilla::tabs::TabThread;
 using mozilla::ipc::TestShellParent;
 using mozilla::ipc::TestShellThread;
 using mozilla::ipc::XPCShellEnvironment;
 
+using mozilla::test::TestParent;
+using mozilla::test::TestProcessParent;
+using mozilla::test::TestThreadChild;
+
 using mozilla::Monitor;
 using mozilla::MonitorAutoEnter;
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 void
 XRE_GetStaticComponents(nsStaticModuleInfo const **aStaticComponents,
                         PRUint32 *aComponentCount)
@@ -255,16 +262,20 @@ XRE_InitChildProcess(int aArgc,
     case GeckoChildProcess_Plugin:
       mainThread = new PluginThreadChild();
       break;
 
     case GeckoChildProcess_Tab:
       mainThread = new TabThread();
       break;
 
+    case GeckoChildProcess_TestHarness:
+      mainThread = new TestThreadChild();
+      break;
+
     case GeckoChildProcess_TestShell:
       mainThread = new TestShellThread();
       break;
 
     default:
       NS_RUNTIMEABORT("Unknown main thread class");
     }
 
@@ -504,8 +515,52 @@ XRE_RunTestShell(int aArgc, char* aArgv[
     TestShellData data = { &result, aArgc, aArgv };
 
     nsresult rv =
       XRE_InitParentProcess(aArgc, aArgv, TestShellMainWrapper, &data);
     NS_ENSURE_SUCCESS(rv, 1);
 
     return result;
 }
+
+//-----------------------------------------------------------------------------
+// TestHarness
+
+static void
+IPCTestHarnessMain(TestProcessParent* subprocess)
+{
+    TestParent parent;
+
+    parent.Open(subprocess->GetChannel());
+    parent.DoStuff();
+}
+
+static void
+IPCTestHarnessLaunchSubprocess(TestProcessParent* subprocess,
+                               MessageLoop* mainLoop)
+{
+    bool launched = subprocess->Launch();
+    NS_ASSERTION(launched, "can't launch subprocess");
+    mainLoop->PostTask(FROM_HERE,
+                       NewRunnableFunction(IPCTestHarnessMain, subprocess));
+}
+
+static void
+IPCTestHarnessPostLaunchSubprocessTask(void* data)
+{
+    TestProcessParent* subprocess = new TestProcessParent();
+    MessageLoop* ioLoop = 
+        BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO);
+    ioLoop->PostTask(FROM_HERE,
+                     NewRunnableFunction(IPCTestHarnessLaunchSubprocess,
+                                         subprocess,
+                                         MessageLoop::current()));
+}
+
+int
+XRE_RunIPCTestHarness(int aArgc, char* aArgv[])
+{
+    nsresult rv =
+        XRE_InitParentProcess(
+            aArgc, aArgv, IPCTestHarnessPostLaunchSubprocessTask, NULL);
+    NS_ENSURE_SUCCESS(rv, 1);
+    return 0;
+}
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -419,16 +419,18 @@ XRE_API(void,
         XRE_FreeAppData, (nsXREAppData *aAppData))
 
 
 enum GeckoChildProcessType {
   GeckoChildProcess_Default,
 
   GeckoChildProcess_Plugin,
   GeckoChildProcess_Tab,
+
+  GeckoChildProcess_TestHarness,
   GeckoChildProcess_TestShell,
 
   GeckoChildProcess_End,
   GeckoChildProcess_Invalid = GeckoChildProcess_End
 };
 
 static const char* const kGeckoChildProcessTypeString[] = {
   "default",
@@ -455,9 +457,13 @@ XRE_API(nsresult,
                                 char* aArgv[],
                                 MainFunction aMainFunction,
                                 void* aMainFunctionExtraData))
 
 XRE_API(int,
         XRE_RunTestShell, (int aArgc,
                            char* aArgv[]))
 
+XRE_API(int,
+        XRE_RunIPCTestHarness, (int aArgc,
+                                char* aArgv[]))
+
 #endif // _nsXULAppAPI_h__