Allow ipcshell to receive a response from its child process
authorBen Turner <bent.mozilla@gmail.com>
Thu, 23 Jul 2009 15:27:17 -0700
changeset 35809 9e835e50d6f34e1269d589ee0bbe87733e95daa6
parent 35808 1aec04173c58095bcf64a5070183a1afa3491807
child 35810 c3dd3518fd704565d054cdcdf9a6b19649b8109d
push idunknown
push userunknown
push dateunknown
milestone1.9.2a1pre
Allow ipcshell to receive a response from its child process
ipc/glue/GeckoThread.cpp
ipc/testshell/TestShell.ipdl
ipc/testshell/TestShellChild.cpp
ipc/testshell/TestShellChild.h
ipc/testshell/TestShellThread.cpp
ipc/testshell/XPCShellEnvironment.cpp
ipc/testshell/XPCShellEnvironment.h
toolkit/xre/nsEmbedFunctions.cpp
--- a/ipc/glue/GeckoThread.cpp
+++ b/ipc/glue/GeckoThread.cpp
@@ -70,21 +70,16 @@ GeckoThread::Init()
   mXREEmbed.Start();
 }
 
 void
 GeckoThread::CleanUp()
 {
   mXREEmbed.Stop();
   NS_LogTerm();
-
-  // Maybe kill process?
-
-  // Call this last according to chrome source comments.
-  ChildThread::CleanUp();
 }
 
 //
 // BrowserProcessSubThread
 //
 
 // Friendly names for the well-known threads.
 static const char* kBrowserThreadNames[BrowserProcessSubThread::ID_COUNT] = {
--- a/ipc/testshell/TestShell.ipdl
+++ b/ipc/testshell/TestShell.ipdl
@@ -32,16 +32,17 @@
  * 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
+sync protocol TestShell
 {
   child:
-    rpc SendCommand(String aCommand);
+    async SendCommand(String aCommand);
+    sync SendCommandWithResponse(String aCommand) returns (String aResponse);
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/testshell/TestShellChild.cpp
+++ b/ipc/testshell/TestShellChild.cpp
@@ -48,18 +48,31 @@ TestShellChild::TestShellChild()
 }
 
 TestShellChild::~TestShellChild()
 {
 
 }
 
 nsresult
-TestShellChild::AnswerSendCommand(const String& aCommand)
+TestShellChild::RecvSendCommand(const String& aCommand)
 {
-  nsresult rv = mXPCShell->EvaluateString(aCommand) ? NS_OK : NS_ERROR_FAILURE;
-
   if (mXPCShell->IsQuitting()) {
-    MessageLoop::current()->Quit();
+    NS_WARNING("Commands sent after quit command issued!");
+    return NS_ERROR_UNEXPECTED;
   }
 
-  return rv;
+  return mXPCShell->EvaluateString(aCommand) ? NS_OK : NS_ERROR_FAILURE;
 }
+
+nsresult
+TestShellChild::RecvSendCommandWithResponse(const String& aCommand,
+                                            String* aResponse)
+{
+  if (mXPCShell->IsQuitting()) {
+    NS_WARNING("Commands sent after quit command issued!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return mXPCShell->EvaluateString(aCommand, aResponse) ?
+         NS_OK :
+         NS_ERROR_FAILURE;
+}
--- a/ipc/testshell/TestShellChild.h
+++ b/ipc/testshell/TestShellChild.h
@@ -47,17 +47,19 @@ class XPCShellEnvironment;
 class TestShellChild : public TestShellProtocolChild
 {
 public:
   typedef mozilla::ipc::String String;
 
   TestShellChild();
   virtual ~TestShellChild();
 
-  virtual nsresult AnswerSendCommand(const String& aCommand);
+  virtual nsresult RecvSendCommand(const String& aCommand);
+  virtual nsresult RecvSendCommandWithResponse(const String& aCommand,
+                                               String* aResponse);
 
   void SetXPCShell(XPCShellEnvironment* aXPCShell) {
     mXPCShell = aXPCShell;
   }
 
 private:
   XPCShellEnvironment* mXPCShell;
 };
--- a/ipc/testshell/TestShellThread.cpp
+++ b/ipc/testshell/TestShellThread.cpp
@@ -54,24 +54,24 @@ TestShellThread::~TestShellThread()
 
 }
 
 void
 TestShellThread::Init()
 {
     GeckoThread::Init();
     mXPCShell = XPCShellEnvironment::CreateEnvironment();
-    if (mXPCShell) {
+    if (mXPCShell && mXPCShell->DefineIPCCommands(&mTestShellChild)) {
         if (mTestShellChild.Open(channel(), owner_loop())) {
             mTestShellChild.SetXPCShell(mXPCShell);
         }
     }
 }
 
 void
 TestShellThread::CleanUp()
 {
-    GeckoThread::CleanUp();
     if (mXPCShell) {
         XPCShellEnvironment::DestroyEnvironment(mXPCShell);
         mTestShellChild.Close();
     }
+    GeckoThread::CleanUp();
 }
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -40,20 +40,16 @@
 #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"
@@ -64,22 +60,24 @@
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIURI.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 
 #include "nsXULAppAPI.h"
 
+#include "TestShellChild.h"
 #include "TestShellParent.h"
 
 #define EXITCODE_RUNTIME_ERROR 3
 #define EXITCODE_FILE_NOT_FOUND 4
 
 using mozilla::ipc::XPCShellEnvironment;
+using mozilla::ipc::TestShellChild;
 using mozilla::ipc::TestShellParent;
 
 namespace {
 
 static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js";
 
 class FullTrustSecMan : public nsIScriptSecurityManager
 {
@@ -1432,74 +1430,148 @@ SendCommand(JSContext *cx,
   }
 
   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);
+  mozilla::ipc::String command(JS_GetStringBytes(str));
+
+  if (!Environment(cx)->DoSendCommand(command)) {
+    JS_ReportError(cx, "Failed to send command!");
+    return JS_FALSE;
+  }
+
+  return JS_TRUE;
+}
+
+static JSBool
+SendCommandWithResponse(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;
+  }
+
+  mozilla::ipc::String command(JS_GetStringBytes(str));
+  mozilla::ipc::String result;
+
+  if (!Environment(cx)->DoSendCommand(command, &result)) {
+    JS_ReportError(cx, "Failed to send command!");
+    return JS_FALSE;
+  }
+
+  JSString* resultStr = JS_NewStringCopyN(cx, result.c_str(), result.length());
+  if (!resultStr) {
+    JS_ReportError(cx, "Failed to convert response to string!");
+    return JS_FALSE;
+  }
+
+  *rval = STRING_TO_JSVAL(resultStr);
+  return JS_TRUE;
 }
 
 } /* anonymous namespace */
 
 bool
-XPCShellEnvironment::DefineSendCommand(TestShellParent* aParent)
+XPCShellEnvironment::DefineIPCCommands(TestShellChild* aChild)
 {
+    NS_ASSERTION(aChild, "Don't hand me null!");
+
+    // XXXbent Nothing here yet, soon though!
+    return true;
+}
+
+bool
+XPCShellEnvironment::DefineIPCCommands(TestShellParent* aParent)
+{
+    NS_ASSERTION(aParent, "Don't hand me null!");
+
     mParent = aParent;
 
+    JSObject* global = GetGlobalObject();
+
     JSAutoRequest ar(mCx);
 
-    JSFunction* fun = JS_DefineFunction(mCx, GetGlobalObject(), "sendCommand",
+    JSFunction* fun = JS_DefineFunction(mCx, global, "sendCommand",
                                         SendCommand, 1, JSPROP_ENUMERATE);
     if (!fun) {
       NS_WARNING("Failed to define sendCommand function!");
       return false;
     }
 
+    fun = JS_DefineFunction(mCx, global, "sendCommandWithResponse",
+                            SendCommandWithResponse, 1, JSPROP_ENUMERATE);
+    if (!fun) {
+      NS_WARNING("Failed to define sendCommandWithResponse function!");
+      return false;
+    }
+
     return true;
 }
 
 JSBool
-XPCShellEnvironment::DoSendCommand(JSString* aCommand)
+XPCShellEnvironment::DoSendCommand(const mozilla::ipc::String& aCommand,
+                                   mozilla::ipc::String* aResult)
 {
-  String str(JS_GetStringBytes(aCommand));
+  nsresult rv = aResult ?
+                mParent->SendSendCommandWithResponse(aCommand, aResult) :
+                mParent->SendSendCommand(aCommand);
 
-  nsresult rv = mParent->CallSendCommand(str);
   return NS_SUCCEEDED(rv) ? JS_TRUE : JS_FALSE;
 }
 
 bool
-XPCShellEnvironment::EvaluateString(const std::string& aString)
+XPCShellEnvironment::EvaluateString(const mozilla::ipc::String& aString,
+                                    mozilla::ipc::String* aResult)
 {
   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()) {
+      if (aResult) {
+          aResult->clear();
+      }
+
       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));
+              const char* bytes = JS_GetStringBytes(str);
+              fprintf(stdout, "%s\n", bytes);
+              if (aResult) {
+                  aResult->assign(bytes);
+              }
           }
       }
   }
 
   JS_DestroyScript(mCx, script);
 
   return true;
 }
--- a/ipc/testshell/XPCShellEnvironment.h
+++ b/ipc/testshell/XPCShellEnvironment.h
@@ -40,41 +40,47 @@
 #include "base/basictypes.h"
 
 #include <string>
 #include <stdio.h>
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsAutoJSValHolder.h"
 
+#include "mozilla/ipc/TestShellProtocol.h"
+
 struct JSContext;
 struct JSObject;
 struct JSPrincipals;
 
 class nsIJSContextStack;
 
 namespace mozilla {
 namespace ipc {
 
+class TestShellChild;
 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);
+    bool DefineIPCCommands(TestShellChild* aChild);
+    bool DefineIPCCommands(TestShellParent* aParent);
 
-    JSBool DoSendCommand(JSString* aCommand);
+    JSBool DoSendCommand(const mozilla::ipc::String& aCommand,
+                         mozilla::ipc::String* aResult = nsnull);
 
-    bool EvaluateString(const std::string& aString);
+    bool EvaluateString(const mozilla::ipc::String& aString,
+                        mozilla::ipc::String* aResult = nsnull);
 
     JSPrincipals* GetPrincipal() {
         return mJSPrincipals;
     }
 
     JSObject* GetGlobalObject() {
         return mGlobalHolder.ToJSObject();
     }
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -430,17 +430,17 @@ TestShellMain(int argc, char** argv)
   NS_ENSURE_TRUE(channel, 1);
 
   TestShellParent testShellParent;
   if (!testShellParent.Open(channel)) {
     NS_WARNING("Failed to open channel!");
     return 1;
   }
 
-  if (!env->DefineSendCommand(&testShellParent)) {
+  if (!env->DefineIPCCommands(&testShellParent)) {
     NS_WARNING("DefineChildObject failed!");
     return 1;
   }
 
   const char* filename = argc > 1 ? argv[1] : nsnull;
   env->Process(filename);
 
   return env->ExitCode();