Adds the ipcshell application to test IPC
authorBen Turner <bent.mozilla@gmail.com>
Sat, 11 Jul 2009 02:33:10 -0400
changeset 35771 ec2881eb7e06471bda1e5c16534cdb945bcaf34a
parent 35770 bbcef80246c85787753bc1ea6d62ef30d1c9ba7f
child 35772 3b2c1dba74e27b6ae61004e7738c4e26e3257650
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
Adds the ipcshell application to test IPC
ipc/Makefile.in
ipc/app/MozillaRuntimeMain.cpp
ipc/glue/GeckoChildProcessHost.h
ipc/glue/IPCMessageUtils.h
ipc/testshell/IPCMessageStart.h
ipc/testshell/Makefile.in
ipc/testshell/TestShell.ipdl
ipc/testshell/TestShellChild.cpp
ipc/testshell/TestShellChild.h
ipc/testshell/TestShellParent.cpp
ipc/testshell/TestShellParent.h
ipc/testshell/TestShellProtocol.h
ipc/testshell/TestShellProtocolChild.h
ipc/testshell/TestShellProtocolParent.h
ipc/testshell/TestShellThread.cpp
ipc/testshell/TestShellThread.h
ipc/testshell/XPCShellEnvironment.cpp
ipc/testshell/XPCShellEnvironment.h
ipc/testshell/app/Makefile.in
ipc/testshell/app/TestShellApp.cpp
toolkit/library/dlldeps-xul.cpp
toolkit/library/libxul-config.mk
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/nsXULAppAPI.h
--- 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 += chromium glue
+DIRS += chromium glue testshell
 TOOL_DIRS = app
 
 include $(topsrcdir)/config/rules.mk
--- a/ipc/app/MozillaRuntimeMain.cpp
+++ b/ipc/app/MozillaRuntimeMain.cpp
@@ -47,26 +47,23 @@
 #endif
 
 #ifdef XP_WIN
 #include <windows.h>
 // we want a wmain entry point
 #include "nsWindowsWMain.cpp"
 #endif
 
-class ScopedLogging
-{
-public:
-    ScopedLogging() { NS_LogInit(); }
-    ~ScopedLogging() { NS_LogTerm(); }
-};
-
 int
 main(int argc, char* argv[])
 {
-    ScopedLogging log;
+#if 0
+    MessageBox(NULL, L"Hi", L"Hi", MB_OK);
+#endif
 
     GeckoChildProcessType proctype =
-        XRE_StringToChildProcessType(argv[argc-1]);
-    nsresult rv = XRE_InitChildProcess(--argc, argv, proctype);
+        XRE_StringToChildProcessType(argv[argc - 1]);
+
+    nsresult rv = XRE_InitChildProcess(argc - 1, argv, proctype);
     NS_ENSURE_SUCCESS(rv, 1);
+
     return 0;
 }
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -56,16 +56,20 @@ public:
   bool Launch(std::vector<std::wstring> aExtraOpts=std::vector<std::wstring>());
 
   // FIXME/cjones: these should probably disappear
   virtual void OnMessageReceived(const IPC::Message& aMsg);
   virtual void OnChannelError();
 
   virtual bool CanShutdown() { return true; }
 
+  IPC::Channel* GetChannel() {
+    return channelp();
+  }
+
 protected:
   GeckoChildProcessType mProcessType;
   FilePath mProcessPath;
 
 #if defined(OS_POSIX)
   base::file_handle_mapping_vector mFileMap;
 #endif
 
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -46,16 +46,18 @@
 // Used by chrome code, we override this here and #ifdef theirs out. See the
 // comments in chrome/common/ipc_message_utils.h for info about this enum.
 enum IPCMessageStart {
   NPAPIProtocolMsgStart = 0,
   NPPProtocolMsgStart = 0,
 
   IFrameEmbeddingProtocolMsgStart,
 
+  TestShellProtocolMsgStart,
+
   LastMsgIndex
 };
 
 COMPILE_ASSERT(LastMsgIndex <= 16, need_to_update_IPC_MESSAGE_MACRO);
 
 namespace IPC {
 
 
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/IPCMessageStart.h
@@ -0,0 +1,9 @@
+
+// CODE GENERATED by ipdl.py. Do not edit.
+
+enum IPCMessageStart {
+
+
+  LastMsgIndex
+};
+
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/Makefile.in
@@ -0,0 +1,75 @@
+# ***** 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 = ipcshell
+LIBRARY_NAME = ipcshell_s
+FORCE_STATIC_LIB = 1
+LIBXUL_LIBRARY = 1
+EXPORT_LIBRARY = 1
+
+EXPORTS_NAMESPACES = mozilla/ipc
+
+EXPORTS_mozilla/ipc = \
+  TestShellChild.h \
+  TestShellParent.h \
+  TestShellProtocol.h \
+  TestShellProtocolChild.h \
+  TestShellProtocolParent.h \
+  TestShellThread.h \
+  XPCShellEnvironment.h \
+  $(NULL)
+
+CPPSRCS += \
+  TestShellChild.cpp \
+  TestShellParent.cpp \
+  TestShellThread.cpp \
+  XPCShellEnvironment.cpp \
+  $(NULL)
+
+LOCAL_INCLUDES += -I$(topsrcdir)/js/src/xpconnect/shell
+
+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/testshell/TestShell.ipdl
@@ -0,0 +1,47 @@
+/* ***** 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 ***** */
+
+namespace mozilla {
+namespace ipc {
+
+rpc protocol TestShell
+{
+  child:
+    rpc SendCommand(String aCommand);
+};
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellChild.cpp
@@ -0,0 +1,65 @@
+/* ***** 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 "TestShellChild.h"
+
+#include "XPCShellEnvironment.h"
+
+using mozilla::ipc::TestShellChild;
+using mozilla::ipc::XPCShellEnvironment;
+
+TestShellChild::TestShellChild()
+: mXPCShell(nsnull)
+{
+
+}
+
+TestShellChild::~TestShellChild()
+{
+
+}
+
+nsresult
+TestShellChild::AnswerSendCommand(const String& aCommand)
+{
+  nsresult rv = mXPCShell->EvaluateString(aCommand) ? NS_OK : NS_ERROR_FAILURE;
+
+  if (mXPCShell->IsQuitting()) {
+    MessageLoop::current()->Quit();
+  }
+
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellChild.h
@@ -0,0 +1,68 @@
+/* ***** 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 ***** */
+
+#ifndef _IPC_TESTSHELL_TESTSHELLCHILD_H_
+#define _IPC_TESTSHELL_TESTSHELLCHILD_H_
+
+#include "mozilla/ipc/TestShellProtocolChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+class XPCShellEnvironment;
+
+class TestShellChild : public TestShellProtocolChild
+{
+public:
+  typedef mozilla::ipc::String String;
+
+  TestShellChild();
+  virtual ~TestShellChild();
+
+  virtual nsresult AnswerSendCommand(const String& aCommand);
+
+  void SetXPCShell(XPCShellEnvironment* aXPCShell) {
+    mXPCShell = aXPCShell;
+  }
+
+private:
+  XPCShellEnvironment* mXPCShell;
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* _IPC_TESTSHELL_TESTSHELLCHILD_H_ */
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellParent.cpp
@@ -0,0 +1,49 @@
+/* ***** 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 "TestShellParent.h"
+
+using mozilla::ipc::TestShellParent;
+
+TestShellParent::TestShellParent()
+{
+
+}
+
+TestShellParent::~TestShellParent()
+{
+
+}
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellParent.h
@@ -0,0 +1,57 @@
+/* ***** 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 ***** */
+
+#ifndef _IPC_TESTSHELL_TESTSHELLPARENT_H_
+#define _IPC_TESTSHELL_TESTSHELLPARENT_H_
+
+#include "mozilla/ipc/TestShellProtocolParent.h"
+
+namespace mozilla {
+namespace ipc {
+
+class TestShellParent : public TestShellProtocolParent
+{
+public:
+  typedef mozilla::ipc::String String;
+
+  TestShellParent();
+  ~TestShellParent();
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* _IPC_TESTSHELL_TESTSHELLPARENT_H_ */
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellProtocol.h
@@ -0,0 +1,91 @@
+//
+// Automatically generated by ipdlc.
+// Edit at your own risk
+//
+
+#ifndef mozilla_ipc_TestShellProtocol_h
+#define mozilla_ipc_TestShellProtocol_h
+
+#include "base/basictypes.h"
+#include "nscore.h"
+#include "IPC/IPCMessageUtils.h"
+#include "mozilla/ipc/MessageTypes.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+namespace mozilla {
+namespace ipc {
+namespace TestShellProtocol {
+
+
+enum State {
+    StateStart = 0,
+    StateError,
+    StateLast
+};
+
+enum MessageType {
+    TestShellProtocolStart = TestShellProtocolMsgStart << 12,
+    TestShellProtocolPreStart = (TestShellProtocolMsgStart << 12) - 1,
+    Msg_SendCommand__ID,
+    Reply_SendCommand__ID,
+    TestShellProtocolEnd
+};
+
+class Msg_SendCommand :
+    public IPC::Message
+{
+private:
+    typedef mozilla::ipc::String String;
+    typedef mozilla::ipc::StringArray StringArray;
+
+public:
+    enum {
+        ID = Msg_SendCommand__ID
+    };
+    Msg_SendCommand(const String& aCommand) :
+        IPC::Message(MSG_ROUTING_NONE, ID, PRIORITY_NORMAL)
+    {
+        IPC::WriteParam(this, aCommand);
+    }
+
+    static bool Read(
+                const Message* msg,
+                String* aCommand)
+    {
+        void* iter = 0;
+
+        if (!(IPC::ReadParam(msg, &(iter), aCommand))) {
+            return false;
+        }
+
+        return true;
+    }
+};
+class Reply_SendCommand :
+    public IPC::Message
+{
+private:
+    typedef mozilla::ipc::String String;
+    typedef mozilla::ipc::StringArray StringArray;
+
+public:
+    enum {
+        ID = Reply_SendCommand__ID
+    };
+    Reply_SendCommand() :
+        IPC::Message(MSG_ROUTING_NONE, ID, PRIORITY_NORMAL)
+    {
+    }
+
+    static bool Read(const Message* msg)
+    {
+        return true;
+    }
+};
+
+
+} // namespace TestShellProtocol
+} // namespace ipc
+} // namespace mozilla
+
+#endif // ifndef mozilla_ipc_TestShellProtocol_h
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellProtocolChild.h
@@ -0,0 +1,141 @@
+//
+// Automatically generated by ipdlc.
+// Edit at your own risk
+//
+
+#ifndef mozilla_ipc_TestShellProtocolChild_h
+#define mozilla_ipc_TestShellProtocolChild_h
+
+#include "mozilla/ipc/TestShellProtocol.h"
+#include "mozilla/ipc/RPCChannel.h"
+
+namespace mozilla {
+namespace ipc {
+
+
+class /*NS_ABSTRACT_CLASS*/ TestShellProtocolChild :
+    public mozilla::ipc::RPCChannel::Listener
+{
+protected:
+    typedef mozilla::ipc::String String;
+    typedef mozilla::ipc::StringArray StringArray;
+
+    virtual nsresult AnswerSendCommand(const String& aCommand) = 0;
+
+private:
+    typedef IPC::Message Message;
+    typedef mozilla::ipc::RPCChannel Channel;
+
+public:
+    TestShellProtocolChild() :
+        mChannel(this)
+    {
+    }
+
+    virtual ~TestShellProtocolChild()
+    {
+    }
+
+    bool Open(
+                IPC::Channel* aChannel,
+                MessageLoop* aThread = 0)
+    {
+        return mChannel.Open(aChannel, aThread);
+    }
+
+    void Close()
+    {
+        mChannel.Close();
+    }
+
+    virtual Result OnMessageReceived(const Message& msg)
+    {
+        switch (msg.type()) {
+        default:
+            {
+                return MsgNotKnown;
+            }
+        }
+    }
+
+    virtual Result OnMessageReceived(
+                const Message& msg,
+                Message*& reply)
+    {
+        switch (msg.type()) {
+        default:
+            {
+                return MsgNotKnown;
+            }
+        }
+    }
+
+    virtual Result OnCallReceived(
+                const Message& msg,
+                Message*& reply)
+    {
+        switch (msg.type()) {
+        case TestShellProtocol::Msg_SendCommand__ID:
+            {
+                String aCommand;
+
+                if (!(TestShellProtocol::Msg_SendCommand::Read(&(msg), &(aCommand)))) {
+                    return MsgPayloadError;
+                }
+                if (NS_FAILED(AnswerSendCommand(aCommand))) {
+                    return MsgValueError;
+                }
+
+                reply = new TestShellProtocol::Reply_SendCommand();
+                reply->set_reply();
+                return MsgProcessed;
+            }
+        default:
+            {
+                return MsgNotKnown;
+            }
+        }
+    }
+
+private:
+    Channel mChannel;
+    int mId;
+    int mPeerId;
+    mozilla::ipc::IProtocolManager* mManager;
+};
+
+
+} // namespace ipc
+} // namespace mozilla
+#if 0
+
+//-----------------------------------------------------------------------------
+// Skeleton implementation of abstract actor class
+
+// Header file contents
+class ActorImpl :
+    public TestShellProtocolChild
+{
+    virtual nsresult AnswerSendCommand(const String& aCommand);
+    ActorImpl();
+    virtual ~ActorImpl();
+};
+
+
+// C++ file contents
+nsresult ActorImpl::AnswerSendCommand(const String& aCommand)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+ActorImpl::ActorImpl()
+{
+}
+
+ActorImpl::~ActorImpl()
+{
+}
+
+#endif // if 0
+
+#endif // ifndef mozilla_ipc_TestShellProtocolChild_h
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellProtocolParent.h
@@ -0,0 +1,134 @@
+//
+// Automatically generated by ipdlc.
+// Edit at your own risk
+//
+
+#ifndef mozilla_ipc_TestShellProtocolParent_h
+#define mozilla_ipc_TestShellProtocolParent_h
+
+#include "mozilla/ipc/TestShellProtocol.h"
+#include "mozilla/ipc/RPCChannel.h"
+
+namespace mozilla {
+namespace ipc {
+
+
+class /*NS_ABSTRACT_CLASS*/ TestShellProtocolParent :
+    public mozilla::ipc::RPCChannel::Listener
+{
+protected:
+    typedef mozilla::ipc::String String;
+    typedef mozilla::ipc::StringArray StringArray;
+
+
+private:
+    typedef IPC::Message Message;
+    typedef mozilla::ipc::RPCChannel Channel;
+
+public:
+    TestShellProtocolParent() :
+        mChannel(this)
+    {
+    }
+
+    virtual ~TestShellProtocolParent()
+    {
+    }
+
+    bool Open(
+                IPC::Channel* aChannel,
+                MessageLoop* aThread = 0)
+    {
+        return mChannel.Open(aChannel, aThread);
+    }
+
+    void Close()
+    {
+        mChannel.Close();
+    }
+
+    nsresult CallSendCommand(const String& aCommand)
+    {
+        Message __reply;
+        Message* __msg;
+        __msg = new TestShellProtocol::Msg_SendCommand(aCommand);
+        __msg->set_routing_id(MSG_ROUTING_CONTROL);
+        if (!(mChannel.Call(__msg, &(__reply)))) {
+            return NS_ERROR_FAILURE;
+        }
+        if (!(TestShellProtocol::Reply_SendCommand::Read(&(__reply)))) {
+            return NS_ERROR_ILLEGAL_VALUE;
+        }
+        return NS_OK;
+    }
+
+    virtual Result OnMessageReceived(const Message& msg)
+    {
+        switch (msg.type()) {
+        default:
+            {
+                return MsgNotKnown;
+            }
+        }
+    }
+
+    virtual Result OnMessageReceived(
+                const Message& msg,
+                Message*& reply)
+    {
+        switch (msg.type()) {
+        default:
+            {
+                return MsgNotKnown;
+            }
+        }
+    }
+
+    virtual Result OnCallReceived(
+                const Message& msg,
+                Message*& reply)
+    {
+        switch (msg.type()) {
+        default:
+            {
+                return MsgNotKnown;
+            }
+        }
+    }
+
+private:
+    Channel mChannel;
+    int mId;
+    int mPeerId;
+    mozilla::ipc::IProtocolManager* mManager;
+};
+
+
+} // namespace ipc
+} // namespace mozilla
+#if 0
+
+//-----------------------------------------------------------------------------
+// Skeleton implementation of abstract actor class
+
+// Header file contents
+class ActorImpl :
+    public TestShellProtocolParent
+{
+    ActorImpl();
+    virtual ~ActorImpl();
+};
+
+
+// C++ file contents
+ActorImpl::ActorImpl()
+{
+}
+
+ActorImpl::~ActorImpl()
+{
+}
+
+#endif // if 0
+
+#endif // ifndef mozilla_ipc_TestShellProtocolParent_h
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellThread.cpp
@@ -0,0 +1,77 @@
+/* ***** 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 "TestShellThread.h"
+
+#include "XPCShellEnvironment.h"
+
+#include "base/message_loop.h"
+
+using mozilla::ipc::TestShellThread;
+using mozilla::ipc::XPCShellEnvironment;
+
+TestShellThread::TestShellThread()
+: mXPCShell(nsnull)
+{
+
+}
+
+TestShellThread::~TestShellThread()
+{
+
+}
+
+void
+TestShellThread::Init()
+{
+    GeckoThread::Init();
+    mXPCShell = XPCShellEnvironment::CreateEnvironment();
+    if (mXPCShell) {
+        if (mTestShellChild.Open(channel(), owner_loop())) {
+            mTestShellChild.SetXPCShell(mXPCShell);
+        }
+    }
+}
+
+void
+TestShellThread::CleanUp()
+{
+    GeckoThread::CleanUp();
+    if (mXPCShell) {
+        XPCShellEnvironment::DestroyEnvironment(mXPCShell);
+        mTestShellChild.Close();
+    }
+}
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellThread.h
@@ -0,0 +1,66 @@
+/* ***** 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 ***** */
+
+#ifndef _IPC_TESTSHELL_TESTSHELLTHREAD_H_
+#define _IPC_TESTSHELL_TESTSHELLTHREAD_H_
+
+#include "mozilla/ipc/GeckoThread.h"
+#include "TestShellChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+class XPCShellEnvironment;
+
+class TestShellThread : public GeckoThread
+{
+public:
+    TestShellThread();
+    ~TestShellThread();
+
+protected:
+    virtual void Init();
+    virtual void CleanUp();
+
+private:
+    XPCShellEnvironment* mXPCShell;
+    TestShellChild mTestShellChild;
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* _IPC_TESTSHELL_TESTSHELLTHREAD_H_ */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -0,0 +1,1505 @@
+/* ***** 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 "XPCShellEnvironment.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_IO_H
+#include <io.h>     /* for isatty() */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>     /* for isatty() */
+#endif
+
+#if defined(XP_MACOSX)
+#include <Foundation/Foundation.h>
+#endif
+
+#include "jsapi.h"
+#include "jscntxt.h"
+#include "jsdbgapi.h"
+#include "jsprf.h"
+
+#include "mozilla/XPCOM.h"
+
+#include "nsIChannel.h"
+#include "nsIClassInfo.h"
+#include "nsIDirectoryService.h"
+#include "nsIJSContextStack.h"
+#include "nsIJSRuntimeService.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIURI.h"
+#include "nsIXPConnect.h"
+#include "nsIXPCScriptable.h"
+
+#include "nsXULAppAPI.h"
+
+#include "TestShellParent.h"
+
+#define EXITCODE_RUNTIME_ERROR 3
+#define EXITCODE_FILE_NOT_FOUND 4
+
+using mozilla::ipc::XPCShellEnvironment;
+using mozilla::ipc::TestShellParent;
+
+namespace {
+
+static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js";
+
+class FullTrustSecMan : public nsIScriptSecurityManager
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIXPCSECURITYMANAGER
+    NS_DECL_NSISCRIPTSECURITYMANAGER
+
+    FullTrustSecMan() { }
+    virtual ~FullTrustSecMan() { }
+
+    void SetSystemPrincipal(nsIPrincipal *aPrincipal) {
+        mSystemPrincipal = aPrincipal;
+    }
+
+private:
+    nsCOMPtr<nsIPrincipal> mSystemPrincipal;
+};
+
+class XPCShellDirProvider : public nsIDirectoryServiceProvider
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+
+    XPCShellDirProvider() { }
+    ~XPCShellDirProvider() { }
+
+    PRBool SetGREDir(const char *dir);
+    void ClearGREDir() { mGREDir = nsnull; }
+
+private:
+    nsCOMPtr<nsILocalFile> mGREDir;
+};
+
+inline XPCShellEnvironment*
+Environment(JSContext* cx)
+{
+    XPCShellEnvironment* env = 
+        static_cast<XPCShellEnvironment*>(JS_GetContextPrivate(cx));
+    NS_ASSERTION(env, "Should never be null!");
+    return env;
+}
+
+static void
+ScriptErrorReporter(JSContext *cx,
+                    const char *message,
+                    JSErrorReport *report)
+{
+    int i, j, k, n;
+    char *prefix = NULL, *tmp;
+    const char *ctmp;
+    JSStackFrame * fp = nsnull;
+    nsCOMPtr<nsIXPConnect> xpc;
+
+    // Don't report an exception from inner JS frames as the callers may intend
+    // to handle it.
+    while ((fp = JS_FrameIterator(cx, &fp))) {
+        if (!JS_IsNativeFrame(cx, fp)) {
+            return;
+        }
+    }
+
+    // In some cases cx->fp is null here so use XPConnect to tell us about inner
+    // frames.
+    if ((xpc = do_GetService(nsIXPConnect::GetCID()))) {
+        nsAXPCNativeCallContext *cc = nsnull;
+        xpc->GetCurrentNativeCallContext(&cc);
+        if (cc) {
+            nsAXPCNativeCallContext *prev = cc;
+            while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
+                PRUint16 lang;
+                if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
+                    lang == nsAXPCNativeCallContext::LANG_JS) {
+                    return;
+                }
+            }
+        }
+    }
+
+    if (!report) {
+        fprintf(stderr, "%s\n", message);
+        return;
+    }
+
+    /* Conditionally ignore reported warnings. */
+    if (JSREPORT_IS_WARNING(report->flags) &&
+        !Environment(cx)->ShouldReportWarnings()) {
+        return;
+    }
+
+    if (report->filename)
+        prefix = JS_smprintf("%s:", report->filename);
+    if (report->lineno) {
+        tmp = prefix;
+        prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
+        JS_free(cx, tmp);
+    }
+    if (JSREPORT_IS_WARNING(report->flags)) {
+        tmp = prefix;
+        prefix = JS_smprintf("%s%swarning: ",
+                             tmp ? tmp : "",
+                             JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
+        JS_free(cx, tmp);
+    }
+
+    /* embedded newlines -- argh! */
+    while ((ctmp = strchr(message, '\n')) != 0) {
+        ctmp++;
+        if (prefix) fputs(prefix, stderr);
+        fwrite(message, 1, ctmp - message, stderr);
+        message = ctmp;
+    }
+    /* If there were no filename or lineno, the prefix might be empty */
+    if (prefix)
+        fputs(prefix, stderr);
+    fputs(message, stderr);
+
+    if (!report->linebuf) {
+        fputc('\n', stderr);
+        goto out;
+    }
+
+    fprintf(stderr, ":\n%s%s\n%s", prefix, report->linebuf, prefix);
+    n = report->tokenptr - report->linebuf;
+    for (i = j = 0; i < n; i++) {
+        if (report->linebuf[i] == '\t') {
+            for (k = (j + 8) & ~7; j < k; j++) {
+                fputc('.', stderr);
+            }
+            continue;
+        }
+        fputc('.', stderr);
+        j++;
+    }
+    fputs("^\n", stderr);
+ out:
+    if (!JSREPORT_IS_WARNING(report->flags)) {
+        Environment(cx)->SetExitCode(EXITCODE_RUNTIME_ERROR);
+    }
+    JS_free(cx, prefix);
+}
+
+JSContextCallback gOldContextCallback = NULL;
+
+static JSBool
+ContextCallback(JSContext *cx,
+                uintN contextOp)
+{
+    if (gOldContextCallback && !gOldContextCallback(cx, contextOp))
+        return JS_FALSE;
+
+    if (contextOp == JSCONTEXT_NEW) {
+        JS_SetErrorReporter(cx, ScriptErrorReporter);
+        JS_SetVersion(cx, JSVERSION_LATEST);
+    }
+    return JS_TRUE;
+}
+
+static JSBool
+Print(JSContext *cx,
+      JSObject *obj,
+      uintN argc,
+      jsval *argv,
+      jsval *rval)
+{
+    uintN i, n;
+    JSString *str;
+
+    for (i = n = 0; i < argc; i++) {
+        str = JS_ValueToString(cx, argv[i]);
+        if (!str)
+            return JS_FALSE;
+        fprintf(stdout, "%s%s", i ? " " : "", JS_GetStringBytes(str));
+        fflush(stdout);
+    }
+    n++;
+    if (n)
+        fputc('\n', stdout);
+    return JS_TRUE;
+}
+
+static JSBool
+GetLine(char *bufp,
+        FILE *file,
+        const char *prompt)
+{
+    char line[256];
+    fprintf(stdout, prompt);
+    fflush(stdout);
+    if (!fgets(line, sizeof line, file))
+        return JS_FALSE;
+    strcpy(bufp, line);
+    return JS_TRUE;
+}
+
+static JSBool
+ReadLine(JSContext *cx,
+         JSObject *obj,
+         uintN argc,
+         jsval *argv,
+         jsval *rval)
+{
+    // While 4096 might be quite arbitrary, this is something to be fixed in
+    // bug 105707. It is also the same limit as in ProcessFile.
+    char buf[4096];
+    JSString *str;
+
+    /* If a prompt was specified, construct the string */
+    if (argc > 0) {
+        str = JS_ValueToString(cx, argv[0]);
+        if (!str)
+            return JS_FALSE;
+        argv[0] = STRING_TO_JSVAL(str);
+    } else {
+        str = JSVAL_TO_STRING(JS_GetEmptyStringValue(cx));
+    }
+
+    /* Get a line from the infile */
+    if (!GetLine(buf, stdin, JS_GetStringBytes(str)))
+        return JS_FALSE;
+
+    /* Strip newline character added by GetLine() */
+    unsigned int buflen = strlen(buf);
+    if (buflen == 0) {
+        if (feof(stdin)) {
+            *rval = JSVAL_NULL;
+            return JS_TRUE;
+        }
+    } else if (buf[buflen - 1] == '\n') {
+        --buflen;
+    }
+
+    /* Turn buf into a JSString */
+    str = JS_NewStringCopyN(cx, buf, buflen);
+    if (!str)
+        return JS_FALSE;
+
+    *rval = STRING_TO_JSVAL(str);
+    return JS_TRUE;
+}
+
+static JSBool
+Dump(JSContext *cx,
+     JSObject *obj,
+     uintN argc,
+     jsval *argv,
+     jsval *rval)
+{
+    JSString *str;
+    if (!argc)
+        return JS_TRUE;
+
+    str = JS_ValueToString(cx, argv[0]);
+    if (!str)
+        return JS_FALSE;
+
+    fputs(JS_GetStringBytes(str), stdout);
+    fflush(stdout);
+    return JS_TRUE;
+}
+
+static JSBool
+Load(JSContext *cx,
+     JSObject *obj,
+     uintN argc,
+     jsval *argv,
+     jsval *rval)
+{
+    uintN i;
+    JSString *str;
+    const char *filename;
+    JSScript *script;
+    JSBool ok;
+    jsval result;
+    FILE *file;
+
+    for (i = 0; i < argc; i++) {
+        str = JS_ValueToString(cx, argv[i]);
+        if (!str)
+            return JS_FALSE;
+        argv[i] = STRING_TO_JSVAL(str);
+        filename = JS_GetStringBytes(str);
+        file = fopen(filename, "r");
+        if (!file) {
+            JS_ReportError(cx, "cannot open file '%s' for reading", filename);
+            return JS_FALSE;
+        }
+        script = JS_CompileFileHandleForPrincipals(cx, obj, filename, file,
+                                                   Environment(cx)->GetPrincipal());
+        fclose(file);
+        if (!script)
+            return JS_FALSE;
+
+        ok = !Environment(cx)->ShouldCompileOnly()
+             ? JS_ExecuteScript(cx, obj, script, &result)
+             : JS_TRUE;
+        JS_DestroyScript(cx, script);
+        if (!ok)
+            return JS_FALSE;
+    }
+    return JS_TRUE;
+}
+
+static JSBool
+Version(JSContext *cx,
+        JSObject *obj,
+        uintN argc,
+        jsval *argv,
+        jsval *rval)
+{
+    if (argc > 0 && JSVAL_IS_INT(argv[0]))
+        *rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(JSVAL_TO_INT(argv[0]))));
+    else
+        *rval = INT_TO_JSVAL(JS_GetVersion(cx));
+    return JS_TRUE;
+}
+
+static JSBool
+BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    fprintf(stdout, "built on %s at %s\n", __DATE__, __TIME__);
+    return JS_TRUE;
+}
+
+static JSBool
+Quit(JSContext *cx,
+     JSObject *obj,
+     uintN argc,
+     jsval *argv,
+     jsval *rval)
+{
+    int exitCode = 0;
+    JS_ConvertArguments(cx, argc, argv, "/ i", &exitCode);
+
+    XPCShellEnvironment* env = Environment(cx);
+    env->SetExitCode(exitCode);
+    env->SetIsQuitting();
+
+    return JS_FALSE;
+}
+
+static JSBool
+DumpXPC(JSContext *cx,
+        JSObject *obj,
+        uintN argc,
+        jsval *argv,
+        jsval *rval)
+{
+    int32 depth = 2;
+
+    if (argc > 0) {
+        if (!JS_ValueToInt32(cx, argv[0], &depth))
+            return JS_FALSE;
+    }
+
+    nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+    if(xpc)
+        xpc->DebugDump((int16)depth);
+    return JS_TRUE;
+}
+
+static JSBool
+GC(JSContext *cx,
+   JSObject *obj,
+   uintN argc,
+   jsval *argv,
+   jsval *rval)
+{
+    JSRuntime *rt;
+    uint32 preBytes;
+
+    rt = cx->runtime;
+    preBytes = rt->gcBytes;
+    JS_GC(cx);
+    fprintf(stdout, "before %lu, after %lu, break %08lx\n",
+           (unsigned long)preBytes, (unsigned long)rt->gcBytes,
+#ifdef XP_UNIX
+           (unsigned long)sbrk(0)
+#else
+           0
+#endif
+           );
+#ifdef JS_GCMETER
+    js_DumpGCStats(rt, stdout);
+#endif
+    return JS_TRUE;
+}
+
+#ifdef DEBUG
+
+static JSBool
+DumpHeap(JSContext *cx,
+         JSObject *obj,
+         uintN argc,
+         jsval *argv,
+         jsval *rval)
+{
+    char *fileName = NULL;
+    void* startThing = NULL;
+    uint32 startTraceKind = 0;
+    void *thingToFind = NULL;
+    size_t maxDepth = (size_t)-1;
+    void *thingToIgnore = NULL;
+    jsval *vp;
+    FILE *dumpFile;
+    JSBool ok;
+
+    vp = &argv[0];
+    if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
+        JSString *str;
+
+        str = JS_ValueToString(cx, *vp);
+        if (!str)
+            return JS_FALSE;
+        *vp = STRING_TO_JSVAL(str);
+        fileName = JS_GetStringBytes(str);
+    }
+
+    vp = &argv[1];
+    if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
+        if (!JSVAL_IS_TRACEABLE(*vp))
+            goto not_traceable_arg;
+        startThing = JSVAL_TO_TRACEABLE(*vp);
+        startTraceKind = JSVAL_TRACE_KIND(*vp);
+    }
+
+    vp = &argv[2];
+    if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
+        if (!JSVAL_IS_TRACEABLE(*vp))
+            goto not_traceable_arg;
+        thingToFind = JSVAL_TO_TRACEABLE(*vp);
+    }
+
+    vp = &argv[3];
+    if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
+        uint32 depth;
+
+        if (!JS_ValueToECMAUint32(cx, *vp, &depth))
+            return JS_FALSE;
+        maxDepth = depth;
+    }
+
+    vp = &argv[4];
+    if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
+        if (!JSVAL_IS_TRACEABLE(*vp))
+            goto not_traceable_arg;
+        thingToIgnore = JSVAL_TO_TRACEABLE(*vp);
+    }
+
+    if (!fileName) {
+        dumpFile = stdout;
+    } else {
+        dumpFile = fopen(fileName, "w");
+        if (!dumpFile) {
+            fprintf(stderr, "dumpHeap: can't open %s: %s\n",
+                    fileName, strerror(errno));
+            return JS_FALSE;
+        }
+    }
+
+    ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind,
+                     maxDepth, thingToIgnore);
+    if (dumpFile != stdout)
+        fclose(dumpFile);
+    return ok;
+
+  not_traceable_arg:
+    fprintf(stderr,
+            "dumpHeap: argument %u is not null or a heap-allocated thing\n",
+            (unsigned)(vp - argv));
+    return JS_FALSE;
+}
+
+#endif /* DEBUG */
+
+static JSBool
+Clear(JSContext *cx,
+      JSObject *obj,
+      uintN argc,
+      jsval *argv,
+      jsval *rval)
+{
+    if (argc > 0 && !JSVAL_IS_PRIMITIVE(argv[0])) {
+        JS_ClearScope(cx, JSVAL_TO_OBJECT(argv[0]));
+    } else {
+        JS_ReportError(cx, "'clear' requires an object");
+        return JS_FALSE;
+    }
+    return JS_TRUE;
+}
+
+JSFunctionSpec gGlobalFunctions[] =
+{
+    {"print",           Print,          0,0,0},
+    {"readline",        ReadLine,       1,0,0},
+    {"load",            Load,           1,0,0},
+    {"quit",            Quit,           0,0,0},
+    {"version",         Version,        1,0,0},
+    {"build",           BuildDate,      0,0,0},
+    {"dumpXPC",         DumpXPC,        1,0,0},
+    {"dump",            Dump,           1,0,0},
+    {"gc",              GC,             0,0,0},
+    {"clear",           Clear,          1,0,0},
+#ifdef DEBUG
+    {"dumpHeap",        DumpHeap,       5,0,0},
+#endif
+#ifdef MOZ_SHARK
+    {"startShark",      js_StartShark,      0,0,0},
+    {"stopShark",       js_StopShark,       0,0,0},
+    {"connectShark",    js_ConnectShark,    0,0,0},
+    {"disconnectShark", js_DisconnectShark, 0,0,0},
+#endif
+#ifdef MOZ_CALLGRIND
+    {"startCallgrind",  js_StartCallgrind,  0,0,0},
+    {"stopCallgrind",   js_StopCallgrind,   0,0,0},
+    {"dumpCallgrind",   js_DumpCallgrind,   1,0,0},
+#endif
+    {nsnull,nsnull,0,0,0}
+};
+
+JSClass gGlobalClass =
+{
+    "global", 0,
+    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
+    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub
+};
+
+static JSBool
+EnvironmentSetProperty(JSContext *cx,
+                       JSObject *obj,
+                       jsval id,
+                       jsval *vp)
+{
+/* XXX porting may be easy, but these don't seem to supply setenv by default */
+#if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS
+    JSString *idstr, *valstr;
+    const char *name, *value;
+    int rv;
+
+    idstr = JS_ValueToString(cx, id);
+    valstr = JS_ValueToString(cx, *vp);
+    if (!idstr || !valstr)
+        return JS_FALSE;
+    name = JS_GetStringBytes(idstr);
+    value = JS_GetStringBytes(valstr);
+#if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX \
+    || defined SCO
+    {
+        char *waste = JS_smprintf("%s=%s", name, value);
+        if (!waste) {
+            JS_ReportOutOfMemory(cx);
+            return JS_FALSE;
+        }
+        rv = putenv(waste);
+#ifdef XP_WIN
+        /*
+         * HPUX9 at least still has the bad old non-copying putenv.
+         *
+         * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
+         * that will crash if you pass it an auto char array (so it must place
+         * its argument directly in the char *environ[] array).
+         */
+        free(waste);
+#endif
+    }
+#else
+    rv = setenv(name, value, 1);
+#endif
+    if (rv < 0) {
+        JS_ReportError(cx, "can't set envariable %s to %s", name, value);
+        return JS_FALSE;
+    }
+    *vp = STRING_TO_JSVAL(valstr);
+#endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */
+    return JS_TRUE;
+}
+
+static JSBool
+EnvironmentEnumerate(JSContext *cx,
+                     JSObject *obj)
+{
+    static JSBool reflected;
+    char **evp, *name, *value;
+    JSString *valstr;
+    JSBool ok;
+
+    if (reflected)
+        return JS_TRUE;
+
+    for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) {
+        value = strchr(name, '=');
+        if (!value)
+            continue;
+        *value++ = '\0';
+        valstr = JS_NewStringCopyZ(cx, value);
+        if (!valstr) {
+            ok = JS_FALSE;
+        } else {
+            ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
+                                   NULL, NULL, JSPROP_ENUMERATE);
+        }
+        value[-1] = '=';
+        if (!ok)
+            return JS_FALSE;
+    }
+
+    reflected = JS_TRUE;
+    return JS_TRUE;
+}
+
+static JSBool
+EnvironmentResolve(JSContext *cx,
+                   JSObject *obj,
+                   jsval id,
+                   uintN flags,
+            JSObject **objp)
+{
+    JSString *idstr, *valstr;
+    const char *name, *value;
+
+    if (flags & JSRESOLVE_ASSIGNING)
+        return JS_TRUE;
+
+    idstr = JS_ValueToString(cx, id);
+    if (!idstr)
+        return JS_FALSE;
+    name = JS_GetStringBytes(idstr);
+    value = getenv(name);
+    if (value) {
+        valstr = JS_NewStringCopyZ(cx, value);
+        if (!valstr)
+            return JS_FALSE;
+        if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
+                               NULL, NULL, JSPROP_ENUMERATE)) {
+            return JS_FALSE;
+        }
+        *objp = obj;
+    }
+    return JS_TRUE;
+}
+
+JSClass gEnvironmentClass =
+{
+    "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
+    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub, EnvironmentSetProperty,
+    EnvironmentEnumerate, (JSResolveOp) EnvironmentResolve, JS_ConvertStub,
+    JS_FinalizeStub
+};
+
+typedef enum JSShellErrNum
+{
+#define MSG_DEF(name, number, count, exception, format) \
+    name = number,
+#include "jsshell.msg"
+#undef MSG_DEF
+    JSShellErr_Limit
+#undef MSGDEF
+} JSShellErrNum;
+
+JSErrorFormatString gErrorFormatString[JSErr_Limit] =
+{
+#define MSG_DEF(name, number, count, exception, format) \
+    { format, count } ,
+#include "jsshell.msg"
+#undef MSG_DEF
+};
+
+static const JSErrorFormatString *
+GetErrorMessage(void *userRef,
+                const char *locale,
+                const uintN errorNumber)
+{
+    if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))
+        return &gErrorFormatString[errorNumber];
+
+    return NULL;
+}
+
+static int
+PrintUsage(void)
+{
+    fprintf(stderr, "%s\n", JS_GetImplementationVersion());
+    fprintf(stderr, "usage: xpcshell [-g gredir] [-PswWxCij] [-v version] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
+    return 2;
+}
+
+static void
+ProcessFile(JSContext *cx,
+            JSObject *obj,
+            const char *filename,
+            FILE *file,
+            JSBool forceTTY)
+{
+    XPCShellEnvironment* env = Environment(cx);
+
+    JSScript *script;
+    jsval result;
+    int lineno, startline;
+    JSBool ok, hitEOF;
+    char *bufp, buffer[4096];
+    JSString *str;
+
+    if (forceTTY) {
+        file = stdin;
+    }
+    else
+#ifdef HAVE_ISATTY
+    if (!isatty(fileno(file)))
+#endif
+    {
+        /*
+         * It's not interactive - just execute it.
+         *
+         * Support the UNIX #! shell hack; gobble the first line if it starts
+         * with '#'.  TODO - this isn't quite compatible with sharp variables,
+         * as a legal js program (using sharp variables) might start with '#'.
+         * But that would require multi-character lookahead.
+         */
+        int ch = fgetc(file);
+        if (ch == '#') {
+            while((ch = fgetc(file)) != EOF) {
+                if(ch == '\n' || ch == '\r')
+                    break;
+            }
+        }
+        ungetc(ch, file);
+
+        JSAutoRequest ar(cx);
+
+        JSScript* script =
+            JS_CompileFileHandleForPrincipals(cx, obj, filename, file,
+                                              env->GetPrincipal());
+        if (script) {
+            if (!env->ShouldCompileOnly())
+                (void)JS_ExecuteScript(cx, obj, script, &result);
+            JS_DestroyScript(cx, script);
+        }
+
+        return;
+    }
+
+    /* It's an interactive filehandle; drop into read-eval-print loop. */
+    lineno = 1;
+    hitEOF = JS_FALSE;
+    do {
+        bufp = buffer;
+        *bufp = '\0';
+
+        JSAutoRequest ar(cx);
+
+        /*
+         * Accumulate lines until we get a 'compilable unit' - one that either
+         * generates an error (before running out of source) or that compiles
+         * cleanly.  This should be whenever we get a complete statement that
+         * coincides with the end of a line.
+         */
+        startline = lineno;
+        do {
+            if (!GetLine(bufp, file, startline == lineno ? "js> " : "")) {
+                hitEOF = JS_TRUE;
+                break;
+            }
+            bufp += strlen(bufp);
+            lineno++;
+        } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
+
+        /* Clear any pending exception from previous failed compiles.  */
+        JS_ClearPendingException(cx);
+        script =
+            JS_CompileScriptForPrincipals(cx, obj, env->GetPrincipal(), buffer,
+                                          strlen(buffer), "typein", startline);
+        if (script) {
+            JSErrorReporter older;
+
+            if (!env->ShouldCompileOnly()) {
+                ok = JS_ExecuteScript(cx, obj, script, &result);
+                if (ok && result != JSVAL_VOID) {
+                    /* Suppress error reports from JS_ValueToString(). */
+                    older = JS_SetErrorReporter(cx, NULL);
+                    str = JS_ValueToString(cx, result);
+                    JS_SetErrorReporter(cx, older);
+
+                    if (str)
+                        fprintf(stdout, "%s\n", JS_GetStringBytes(str));
+                    else
+                        ok = JS_FALSE;
+                }
+            }
+            JS_DestroyScript(cx, script);
+        }
+    } while (!hitEOF && !env->IsQuitting());
+
+    fprintf(stdout, "\n");
+}
+
+} /* anonymous namespace */
+
+NS_INTERFACE_MAP_BEGIN(FullTrustSecMan)
+    NS_INTERFACE_MAP_ENTRY(nsIXPCSecurityManager)
+    NS_INTERFACE_MAP_ENTRY(nsIScriptSecurityManager)
+    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCSecurityManager)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(FullTrustSecMan)
+NS_IMPL_RELEASE(FullTrustSecMan)
+
+NS_IMETHODIMP
+FullTrustSecMan::CanCreateWrapper(JSContext * aJSContext,
+                                  const nsIID & aIID,
+                                  nsISupports *aObj,
+                                  nsIClassInfo *aClassInfo,
+                                  void * *aPolicy)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CanCreateInstance(JSContext * aJSContext,
+                                   const nsCID & aCID)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CanGetService(JSContext * aJSContext,
+                               const nsCID & aCID)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CanAccess(PRUint32 aAction,
+                           nsAXPCNativeCallContext *aCallContext,
+                           JSContext * aJSContext,
+                           JSObject * aJSObject,
+                           nsISupports *aObj,
+                           nsIClassInfo *aClassInfo,
+                           jsval aName,
+                           void * *aPolicy)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckPropertyAccess(JSContext * aJSContext,
+                                     JSObject * aJSObject,
+                                     const char *aClassName,
+                                     jsval aProperty,
+                                     PRUint32 aAction)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckConnect(JSContext * aJSContext,
+                              nsIURI *aTargetURI,
+                              const char *aClassName,
+                              const char *aProperty)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckLoadURIFromScript(JSContext * cx,
+                                        nsIURI *uri)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckLoadURIWithPrincipal(nsIPrincipal *aPrincipal,
+                                           nsIURI *uri,
+                                           PRUint32 flags)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckLoadURI(nsIURI *from,
+                              nsIURI *uri,
+                              PRUint32 flags)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckLoadURIStrWithPrincipal(nsIPrincipal *aPrincipal,
+                                              const nsACString & uri,
+                                              PRUint32 flags)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckLoadURIStr(const nsACString & from,
+                                 const nsACString & uri,
+                                 PRUint32 flags)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckFunctionAccess(JSContext * cx,
+                                     void * funObj,
+                                     void * targetObj)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CanExecuteScripts(JSContext * cx,
+                                   nsIPrincipal *principal,
+                                   PRBool *_retval)
+{
+    *_retval = PR_TRUE;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::GetSubjectPrincipal(nsIPrincipal **_retval)
+{
+    NS_IF_ADDREF(*_retval = mSystemPrincipal);
+    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::GetSystemPrincipal(nsIPrincipal **_retval)
+{
+    NS_IF_ADDREF(*_retval = mSystemPrincipal);
+    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::GetCertificatePrincipal(const nsACString & aCertFingerprint,
+                                         const nsACString & aSubjectName,
+                                         const nsACString & aPrettyName,
+                                         nsISupports *aCert,
+                                         nsIURI *aURI,
+                                         nsIPrincipal **_retval)
+{
+    NS_IF_ADDREF(*_retval = mSystemPrincipal);
+    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::GetCodebasePrincipal(nsIURI *aURI,
+                                      nsIPrincipal **_retval)
+{
+    NS_IF_ADDREF(*_retval = mSystemPrincipal);
+    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::RequestCapability(nsIPrincipal *principal,
+                                   const char *capability,
+                                   PRInt16 *_retval)
+{
+    *_retval = nsIPrincipal::ENABLE_GRANTED;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::IsCapabilityEnabled(const char *capability,
+                                     PRBool *_retval)
+{
+    *_retval = PR_TRUE;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::EnableCapability(const char *capability)
+{
+    return NS_OK;;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::RevertCapability(const char *capability)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::DisableCapability(const char *capability)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::SetCanEnableCapability(const nsACString & certificateFingerprint,
+                                        const char *capability,
+                                        PRInt16 canEnable)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::GetObjectPrincipal(JSContext * cx,
+                                    JSObject * obj,
+                                    nsIPrincipal **_retval)
+{
+    NS_IF_ADDREF(*_retval = mSystemPrincipal);
+    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::SubjectPrincipalIsSystem(PRBool *_retval)
+{
+    *_retval = PR_TRUE;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckSameOrigin(JSContext * aJSContext,
+                                 nsIURI *aTargetURI)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::CheckSameOriginURI(nsIURI *aSourceURI,
+                                    nsIURI *aTargetURI,
+                                    PRBool reportError)
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::GetPrincipalFromContext(JSContext * cx,
+                                         nsIPrincipal **_retval)
+{
+    NS_IF_ADDREF(*_retval = mSystemPrincipal);
+    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::GetChannelPrincipal(nsIChannel *aChannel,
+                                     nsIPrincipal **_retval)
+{
+    NS_IF_ADDREF(*_retval = mSystemPrincipal);
+    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FullTrustSecMan::IsSystemPrincipal(nsIPrincipal *aPrincipal,
+                                   PRBool *_retval)
+{
+    *_retval = aPrincipal == mSystemPrincipal;
+    return NS_OK;
+}
+
+NS_IMETHODIMP_(nsIPrincipal *)
+FullTrustSecMan::GetCxSubjectPrincipal(JSContext *cx)
+{
+    return mSystemPrincipal;
+}
+
+NS_IMETHODIMP_(nsIPrincipal *)
+FullTrustSecMan::GetCxSubjectPrincipalAndFrame(JSContext *cx,
+                                               JSStackFrame **fp)
+{
+    *fp = nsnull;
+    return mSystemPrincipal;
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+XPCShellDirProvider::AddRef()
+{
+    return 2;
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+XPCShellDirProvider::Release()
+{
+    return 1;
+}
+
+NS_IMPL_QUERY_INTERFACE1(XPCShellDirProvider, nsIDirectoryServiceProvider)
+
+PRBool
+XPCShellDirProvider::SetGREDir(const char *dir)
+{
+    nsresult rv = XRE_GetFileFromPath(dir, getter_AddRefs(mGREDir));
+    return NS_SUCCEEDED(rv);
+}
+
+NS_IMETHODIMP
+XPCShellDirProvider::GetFile(const char *prop,
+                             PRBool *persistent,
+                             nsIFile* *result)
+{
+    if (mGREDir && !strcmp(prop, NS_GRE_DIR)) {
+        *persistent = PR_TRUE;
+        NS_ADDREF(*result = mGREDir);
+        return NS_OK;
+    }
+
+    return NS_ERROR_FAILURE;
+}
+
+// static
+XPCShellEnvironment*
+XPCShellEnvironment::CreateEnvironment()
+{
+    XPCShellEnvironment* env = new XPCShellEnvironment();
+    if (env && !env->Init()) {
+        delete env;
+        env = nsnull;
+    }
+    return env;
+}
+
+// static
+void
+XPCShellEnvironment::DestroyEnvironment(XPCShellEnvironment* aEnv)
+{
+    delete aEnv;
+}
+
+XPCShellEnvironment::XPCShellEnvironment()
+:   mCx(NULL),
+    mJSPrincipals(NULL),
+    mExitCode(0),
+    mQuitting(JS_FALSE),
+    mReportWarnings(JS_TRUE),
+    mCompileOnly(JS_FALSE),
+    mParent(nsnull)
+{
+}
+
+XPCShellEnvironment::~XPCShellEnvironment()
+{
+    if (mCx) {
+        JS_BeginRequest(mCx);
+
+        JSObject* global = GetGlobalObject();
+        if (global) {
+            JS_ClearScope(mCx, global);
+        }
+        mGlobalHolder.Release();
+
+        JS_GC(mCx);
+
+        if (mCxStack) {
+            JSContext *oldCx;
+            mCxStack->Pop(&oldCx);
+            NS_ASSERTION(oldCx == mCx, "JS thread context push/pop mismatch");
+
+            JS_GC(mCx);
+        }
+
+        if (mJSPrincipals) {
+            JSPRINCIPALS_DROP(mCx, mJSPrincipals);
+        }
+
+        JSRuntime* rt = gOldContextCallback ? JS_GetRuntime(mCx) : NULL;
+
+        JS_DestroyContext(mCx);
+
+        if (gOldContextCallback) {
+            NS_ASSERTION(rt, "Should never be null!");
+            JS_SetContextCallback(rt, gOldContextCallback);
+            gOldContextCallback = NULL;
+        }
+    }
+}
+
+bool
+XPCShellEnvironment::Init()
+{
+    nsresult rv;
+
+#ifdef HAVE_SETBUF
+    // unbuffer stdout so that output is in the correct order; note that stderr
+    // is unbuffered by default
+    setbuf(stdout, 0);
+#endif
+
+    nsCOMPtr<nsIJSRuntimeService> rtsvc =
+        do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
+    if (!rtsvc) {
+        NS_ERROR("failed to get nsJSRuntimeService!");
+        return false;
+    }
+
+    JSRuntime *rt;
+    if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
+        NS_ERROR("failed to get JSRuntime from nsJSRuntimeService!");
+        return false;
+    }
+
+    if (!mGlobalHolder.Hold(rt)) {
+        NS_ERROR("Can't protect global object!");
+        return false;
+    }
+
+    gOldContextCallback = JS_SetContextCallback(rt, ContextCallback);
+
+    JSContext *cx = JS_NewContext(rt, 8192);
+    if (!cx) {
+        NS_ERROR("JS_NewContext failed!");
+
+        JS_SetContextCallback(rt, gOldContextCallback);
+        gOldContextCallback = NULL;
+
+        return false;
+    }
+    mCx = cx;
+
+    JS_SetContextPrivate(cx, this);
+
+    nsCOMPtr<nsIXPConnect> xpc =
+      do_GetService(nsIXPConnect::GetCID());
+    if (!xpc) {
+        NS_ERROR("failed to get nsXPConnect service!");
+        return false;
+    }
+
+    nsRefPtr<FullTrustSecMan> secman(new FullTrustSecMan());
+    xpc->SetSecurityManagerForJSContext(cx, secman, 0xFFFF);
+
+    nsCOMPtr<nsIPrincipal> principal;
+
+    nsCOMPtr<nsIScriptSecurityManager> securityManager =
+        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+    if (NS_SUCCEEDED(rv) && securityManager) {
+        rv = securityManager->GetSystemPrincipal(getter_AddRefs(principal));
+        if (NS_FAILED(rv)) {
+            fprintf(stderr, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n");
+        } else {
+            // fetch the JS principals and stick in a global
+            rv = principal->GetJSPrincipals(cx, &mJSPrincipals);
+            if (NS_FAILED(rv)) {
+                fprintf(stderr, "+++ Failed to obtain JS principals from SystemPrincipal.\n");
+            }
+            secman->SetSystemPrincipal(principal);
+        }
+    } else {
+        fprintf(stderr, "+++ Failed to get ScriptSecurityManager service, running without principals");
+    }
+
+    nsCOMPtr<nsIJSContextStack> cxStack =
+        do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+    if (!cxStack) {
+        NS_ERROR("failed to get the nsThreadJSContextStack service!");
+        return false;
+    }
+
+    if(NS_FAILED(cxStack->Push(cx))) {
+        NS_ERROR("failed to push the current JSContext on the nsThreadJSContextStack!");
+        return false;
+    }
+    mCxStack = cxStack;
+
+    nsCOMPtr<nsIXPCScriptable> backstagePass;
+    rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass));
+    if (NS_FAILED(rv)) {
+        NS_ERROR("Failed to get backstage pass from rtsvc!");
+        return false;
+    }
+
+    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+    rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
+                                              NS_GET_IID(nsISupports),
+                                              nsIXPConnect::
+                                                  FLAG_SYSTEM_GLOBAL_OBJECT,
+                                              getter_AddRefs(holder));
+    if (NS_FAILED(rv)) {
+        NS_ERROR("InitClassesWithNewWrappedGlobal failed!");
+        return false;
+    }
+
+    JSObject *globalObj;
+    rv = holder->GetJSObject(&globalObj);
+    if (NS_FAILED(rv)) {
+        NS_ERROR("Failed to get global JSObject!");
+        return false;
+    }
+
+
+    {
+        JSAutoRequest ar(cx);
+
+        if (!JS_DefineFunctions(cx, globalObj, gGlobalFunctions)) {
+            NS_ERROR("JS_DefineFunctions failed!");
+            return false;
+        }
+
+#if 0 // Just until we care enough to get then environment strings.
+        JSObject *envObj = JS_DefineObject(cx, globalObj, "environment",
+                                           &gEnvironmentClass, NULL, 0);
+        if (!envObj || !JS_SetPrivate(cx, envObj, envp)) {
+            NS_ERROR("Failed to make environment object!");
+            return false;
+        }
+#endif
+
+    }
+
+    mGlobalHolder = globalObj;
+
+    FILE* runtimeScriptFile = fopen(kDefaultRuntimeScriptFilename, "r");
+    if (runtimeScriptFile) {
+        fprintf(stdout, "[loading '%s'...]\n", kDefaultRuntimeScriptFilename);
+        ProcessFile(cx, globalObj, kDefaultRuntimeScriptFilename,
+                    runtimeScriptFile, JS_FALSE);
+        fclose(runtimeScriptFile);
+    }
+
+    return true;
+}
+
+void
+XPCShellEnvironment::Process(const char* aFilename,
+                             JSBool aIsInteractive)
+{
+    NS_ASSERTION(GetGlobalObject(), "Should never be null!");
+
+    FILE* file;
+    if (!aFilename || aIsInteractive) {
+        file = stdin;
+    } else {
+        file = fopen(aFilename, "r");
+        if (!file) {
+            JS_ReportErrorNumber(mCx, GetErrorMessage, NULL,
+                                 JSSMSG_CANT_OPEN,
+                                 aFilename, strerror(errno));
+            mExitCode = EXITCODE_FILE_NOT_FOUND;
+            return;
+        }
+    }
+
+    ProcessFile(mCx, GetGlobalObject(), aFilename, file, aIsInteractive);
+    if (file != stdin)
+        fclose(file);
+}
+
+namespace {
+
+static JSBool
+SendCommand(JSContext *cx,
+            JSObject *obj,
+            uintN argc,
+            jsval *argv,
+            jsval *rval)
+{
+  if (argc != 1) {
+    JS_ReportError(cx, "Function takes only one argument!");
+    return JS_FALSE;
+  }
+
+  JSString* str = JS_ValueToString(cx, argv[0]);
+  if (!str) {
+    JS_ReportError(cx, "Could not convert argument to string!");
+    return JS_FALSE;
+  }
+
+  return Environment(cx)->DoSendCommand(str);
+}
+
+} /* anonymous namespace */
+
+bool
+XPCShellEnvironment::DefineSendCommand(TestShellParent* aParent)
+{
+    mParent = aParent;
+
+    JSAutoRequest ar(mCx);
+
+    JSFunction* fun = JS_DefineFunction(mCx, GetGlobalObject(), "sendCommand",
+                                        SendCommand, 1, JSPROP_ENUMERATE);
+    if (!fun) {
+      NS_WARNING("Failed to define sendCommand function!");
+      return false;
+    }
+
+    return true;
+}
+
+JSBool
+XPCShellEnvironment::DoSendCommand(JSString* aCommand)
+{
+  String str(JS_GetStringBytes(aCommand));
+
+  nsresult rv = mParent->CallSendCommand(str);
+  return NS_SUCCEEDED(rv) ? JS_TRUE : JS_FALSE;
+}
+
+bool
+XPCShellEnvironment::EvaluateString(const std::string& aString)
+{
+  JSAutoRequest ar(mCx);
+
+  JS_ClearPendingException(mCx);
+
+  JSObject* global = GetGlobalObject();
+
+  JSScript* script =
+      JS_CompileScriptForPrincipals(mCx, global, GetPrincipal(),
+                                    aString.c_str(), aString.length(),
+                                    "typein", 0);
+  if (!script) {
+     return false;
+  }
+
+  if (!ShouldCompileOnly()) {
+      jsval result;
+      JSBool ok = JS_ExecuteScript(mCx, global, script, &result);
+      if (ok && result != JSVAL_VOID) {
+          JSErrorReporter old = JS_SetErrorReporter(mCx, NULL);
+          JSString* str = JS_ValueToString(mCx, result);
+          JS_SetErrorReporter(mCx, old);
+
+          if (str) {
+              fprintf(stdout, "%s\n", JS_GetStringBytes(str));
+          }
+      }
+  }
+
+  JS_DestroyScript(mCx, script);
+
+  return true;
+}
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/XPCShellEnvironment.h
@@ -0,0 +1,133 @@
+/* ***** 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 ***** */
+
+#ifndef _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_
+#define _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_
+
+#include "base/basictypes.h"
+
+#include <string>
+#include <stdio.h>
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsAutoJSValHolder.h"
+
+struct JSContext;
+struct JSObject;
+struct JSPrincipals;
+
+class nsIJSContextStack;
+
+namespace mozilla {
+namespace ipc {
+
+class TestShellParent;
+
+class XPCShellEnvironment
+{
+public:
+    static XPCShellEnvironment* CreateEnvironment();
+    static void DestroyEnvironment(XPCShellEnvironment* aEnv);
+
+    void Process(const char* aFilename = nsnull,
+                 JSBool aIsInteractive = JS_FALSE);
+
+    bool DefineSendCommand(TestShellParent* aParent);
+
+    JSBool DoSendCommand(JSString* aCommand);
+
+    bool EvaluateString(const std::string& aString);
+
+    JSPrincipals* GetPrincipal() {
+        return mJSPrincipals;
+    }
+
+    JSObject* GetGlobalObject() {
+        return mGlobalHolder.ToJSObject();
+    }
+
+    void SetExitCode(int aExitCode) {
+        mExitCode = aExitCode;
+    }
+    int ExitCode() {
+        return mExitCode;
+    }
+
+    void SetIsQuitting() {
+        mQuitting = JS_TRUE;
+    }
+    JSBool IsQuitting() {
+        return mQuitting;
+    }
+
+    void SetShouldReportWarnings(JSBool aReportWarnings) {
+        mReportWarnings = aReportWarnings;
+    }
+    JSBool ShouldReportWarnings() {
+        return mReportWarnings;
+    }
+
+    void SetShouldCompoleOnly(JSBool aCompileOnly) {
+        mCompileOnly = aCompileOnly;
+    }
+    JSBool ShouldCompileOnly() {
+        return mCompileOnly;
+    }
+
+protected:
+    XPCShellEnvironment();
+    ~XPCShellEnvironment();
+
+    bool Init();
+
+private:
+    JSContext* mCx;
+    nsAutoJSValHolder mGlobalHolder;
+    nsCOMPtr<nsIJSContextStack> mCxStack;
+    JSPrincipals* mJSPrincipals;
+
+    int mExitCode;
+    JSBool mQuitting;
+    JSBool mReportWarnings;
+    JSBool mCompileOnly;
+
+    TestShellParent* mParent;
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_ */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/app/Makefile.in
@@ -0,0 +1,75 @@
+# ***** 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 = ipcshell
+PROGRAM = $(MODULE)$(BIN_SUFFIX)
+ENABLE_CXX_EXCEPTIONS = 1
+
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+
+CPPSRCS = \
+  TestShellApp.cpp \
+  $(NULL)
+
+LIBS = \
+  $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
+  $(LIBXUL_LIBS) \
+  $(MOZ_JS_LIBS) \
+  $(NSPR_LIBS) \
+  $(DEPTH)/ipc/testshell/ipcshell_s.$(LIB_SUFFIX) \
+  $(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/testshell/app/TestShellApp.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_RunTestShell(argc, argv);
+}
--- a/toolkit/library/dlldeps-xul.cpp
+++ b/toolkit/library/dlldeps-xul.cpp
@@ -44,9 +44,14 @@ void xxxNeverCalledXUL()
   XRE_GetStaticComponents(nsnull, nsnull);
   XRE_LockProfileDirectory(nsnull, nsnull);
   XRE_InitEmbedding(nsnull, nsnull, nsnull, nsnull, 0);
   XRE_NotifyProfile();
   XRE_TermEmbedding();
   XRE_CreateAppData(nsnull, nsnull);
   XRE_ParseAppData(nsnull, nsnull);
   XRE_FreeAppData(nsnull);
+  XRE_ChildProcessTypeToString(GeckoChildProcess_Default);
+  XRE_StringToChildProcessType("");
+  XRE_InitChildProcess(0, nsnull);
+  XRE_InitParentProcess(0, nsnull, nsnull, nsnull);
+  XRE_RunTestShell(0, nsnull);
 }
--- a/toolkit/library/libxul-config.mk
+++ b/toolkit/library/libxul-config.mk
@@ -92,16 +92,17 @@ endif
 
 # dependent libraries
 ifdef MOZ_IPC
 STATIC_LIBS += \
   domipc_s \
   domplugins_s \
   mozipc_s \
   chromium_s \
+  ipcshell_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
@@ -51,37 +51,49 @@
 #include "nsIAppStartupNotifier.h"
 #include "nsIDirectoryService.h"
 #include "nsILocalFile.h"
 #include "nsIToolkitChromeRegistry.h"
 #include "nsIToolkitProfile.h"
 
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsAppRunner.h"
+#include "nsAutoRef.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsStaticComponents.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsXREDirProvider.h"
 
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/GeckoThread.h"
 #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/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::Monitor;
+using mozilla::MonitorAutoEnter;
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 void
 XRE_GetStaticComponents(nsStaticModuleInfo const **aStaticComponents,
                         PRUint32 *aComponentCount)
 {
   *aStaticComponents = kPStaticModules;
@@ -198,25 +210,21 @@ XRE_ChildProcessTypeToString(GeckoChildP
 }
 
 GeckoChildProcessType
 XRE_StringToChildProcessType(const char* aProcessTypeString)
 {
   for (int i = 0;
        i < (int) NS_ARRAY_LENGTH(kGeckoChildProcessTypeString);
        ++i) {
-    const char* procString = kGeckoChildProcessTypeString[i];
-    if (!procString) {
-      return GeckoChildProcess_Invalid;
-    }
-    if (!strcmp(procString, aProcessTypeString)) {
+    if (!strcmp(kGeckoChildProcessTypeString[i], aProcessTypeString)) {
       return static_cast<GeckoChildProcessType>(i);
     }
   }
-  NS_NOTREACHED("error");
+  return GeckoChildProcess_Invalid;
 }
 
 nsresult
 XRE_InitChildProcess(int aArgc,
                      char* aArgv[],
                      GeckoChildProcessType aProcess)
 {
   NS_ENSURE_ARG_MIN(aArgc, 1);
@@ -232,31 +240,35 @@ XRE_InitChildProcess(int aArgc,
 #endif
   }
 
   base::AtExitManager exitManager;
   CommandLine::Init(aArgc, aArgv);
   MessageLoopForIO mainMessageLoop;
 
   {
-    GeckoThread* mainThread;
+    ChildThread* mainThread;
 
     switch (aProcess) {
     case GeckoChildProcess_Default:
       mainThread = new GeckoThread();
       break;
 
     case GeckoChildProcess_Plugin:
       mainThread = new PluginThreadChild();
       break;
 
     case GeckoChildProcess_Tab:
       mainThread = new TabThread();
       break;
 
+    case GeckoChildProcess_TestShell:
+      mainThread = new TestShellThread();
+      break;
+
     default:
       NS_RUNTIMEABORT("Unknown main thread class");
     }
 
     ChildProcess process(mainThread);
 
     // Do IPC event loop
     MessageLoop::current()->Run();
@@ -354,30 +366,146 @@ XRE_InitParentProcess(int aArgc,
   return NS_OK;
 }
 
 namespace {
 
 class CreateChildProcess : public Task
 {
 public:
+  CreateChildProcess(Monitor* aMonitor,
+                     IPC::Channel** aChannelPtr)
+  : mMonitor(aMonitor),
+    mChannelPtr(aChannelPtr)
+  {
+    NS_ASSERTION(aMonitor, "Null ptr!");
+  }
+
   virtual void Run() {
-    GeckoChildProcessHost* host = new GeckoChildProcessHost();
-    if (!host->Launch()) {
-      delete host;
+    GeckoChildProcessHost* host =
+      new GeckoChildProcessHost(GeckoChildProcess_TestShell);
+    if (host) {
+      if (!host->Launch()) {
+        delete host;
+      }
+      // ChildProcessHost deletes itself once the child process exits, on
+      // windows at least...
+      *mChannelPtr = host->GetChannel();
     }
-    // ChildProcessHost deletes itself once the child process exits, on windows
-    // at least...
+
+    MonitorAutoEnter ae(*mMonitor);
+    mMonitor->Notify();
+  }
+
+private:
+  Monitor* mMonitor;
+  IPC::Channel** mChannelPtr;
+};
+
+class QuitRunnable : public nsRunnable
+{
+public:
+  NS_IMETHOD Run() {
+    nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
+    NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
+
+    return appShell->Exit();
+  }
+};
+
+NS_SPECIALIZE_TEMPLATE
+class nsAutoRefTraits<XPCShellEnvironment> :
+  public nsPointerRefTraits<XPCShellEnvironment>
+{
+public:
+  void Release(XPCShellEnvironment* aEnv) {
+    XPCShellEnvironment::DestroyEnvironment(aEnv);
   }
 };
 
+IPC::Channel*
+LaunchTestShellChildProcess()
+{
+  Monitor mon("LaunchChildProcess monitor");
+
+  IPC::Channel* channel = nsnull;
+
+  CreateChildProcess* creator = new CreateChildProcess(&mon, &channel);
+  NS_ENSURE_TRUE(creator, nsnull);
+
+  MessageLoop* ioLoop = 
+      BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO);
+  NS_ENSURE_TRUE(ioLoop, nsnull);
+
+  {
+    MonitorAutoEnter ae(mon);
+
+    ioLoop->PostTask(FROM_HERE, creator);
+    mon.Wait();
+  }
+
+  return channel;
+}
+
+int
+TestShellMain(int argc, char** argv)
+{
+  // Make sure we quit when we exit this function.
+  nsIRunnable* quitRunnable = new QuitRunnable();
+  NS_ENSURE_TRUE(quitRunnable, 1);
+
+  nsresult rv = NS_DispatchToCurrentThread(quitRunnable);
+  NS_ENSURE_SUCCESS(rv, 1);
+
+  nsAutoRef<XPCShellEnvironment> env(XPCShellEnvironment::CreateEnvironment());
+  NS_ENSURE_TRUE(env, 1);
+
+  IPC::Channel* channel = LaunchTestShellChildProcess();
+  NS_ENSURE_TRUE(channel, 1);
+
+  TestShellParent testShellParent;
+  if (!testShellParent.Open(channel)) {
+    NS_WARNING("Failed to open channel!");
+    return 1;
+  }
+
+  if (!env->DefineSendCommand(&testShellParent)) {
+    NS_WARNING("DefineChildObject failed!");
+    return 1;
+  }
+
+  const char* filename = argc > 1 ? argv[1] : nsnull;
+  env->Process(filename);
+
+  return env->ExitCode();
+}
+
+struct TestShellData {
+  int* result;
+  int argc;
+  char** argv;
+};
+
+void
+TestShellMainWrapper(void* aData)
+{
+  NS_ASSERTION(aData, "Don't give me a null pointer!");
+  TestShellData& testShellData = *static_cast<TestShellData*>(aData);
+
+  *testShellData.result =
+    TestShellMain(testShellData.argc, testShellData.argv);
+}
+
 } /* anonymous namespace */
 
-nsresult
-XRE_LaunchChildProcess()
+int
+XRE_RunTestShell(int aArgc, char* aArgv[])
 {
-  MessageLoop* ioLoop = 
-      BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO);
-  NS_ENSURE_TRUE(ioLoop, NS_ERROR_NOT_INITIALIZED);
+    int result;
+
+    TestShellData data = { &result, aArgc, aArgv };
 
-  ioLoop->PostTask(FROM_HERE, new CreateChildProcess());
-  return NS_OK;
+    nsresult rv =
+      XRE_InitParentProcess(aArgc, aArgv, TestShellMainWrapper, &data);
+    NS_ENSURE_SUCCESS(rv, 1);
+
+    return result;
 }
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -419,26 +419,27 @@ XRE_API(void,
         XRE_FreeAppData, (nsXREAppData *aAppData))
 
 
 enum GeckoChildProcessType {
   GeckoChildProcess_Default,
 
   GeckoChildProcess_Plugin,
   GeckoChildProcess_Tab,
+  GeckoChildProcess_TestShell,
 
   GeckoChildProcess_End,
   GeckoChildProcess_Invalid = GeckoChildProcess_End
 };
 
 static const char* const kGeckoChildProcessTypeString[] = {
   "default",
   "plugin",
   "tab",
-  0
+  "testshell"
 };
 
 XRE_API(const char*,
         XRE_ChildProcessTypeToString, (GeckoChildProcessType aProcessType))
 
 XRE_API(GeckoChildProcessType,
         XRE_StringToChildProcessType, (const char* aProcessTypeString))
 
@@ -450,12 +451,13 @@ XRE_API(nsresult,
 typedef void (*MainFunction)(void* aData);
 
 XRE_API(nsresult,
         XRE_InitParentProcess, (int aArgc,
                                 char* aArgv[],
                                 MainFunction aMainFunction,
                                 void* aMainFunctionExtraData))
 
-XRE_API(nsresult,
-        XRE_LaunchChildProcess, ())
+XRE_API(int,
+        XRE_RunTestShell, (int aArgc,
+                           char* aArgv[]))
 
 #endif // _nsXULAppAPI_h__