--- a/dom/ipc/ContentProcessChild.cpp
+++ b/dom/ipc/ContentProcessChild.cpp
@@ -1,16 +1,20 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: sw=4 ts=4 et : */
#include "ContentProcessChild.h"
#include "TabChild.h"
#include "mozilla/ipc/TestShellChild.h"
+#include "mozilla/XPCOM.h"
+#include "nsXPFEComponentsCID.h"
+#include "nsIAppStartup.h"
+
using namespace mozilla::ipc;
namespace mozilla {
namespace dom {
ContentProcessChild* ContentProcessChild::sSingleton;
ContentProcessChild::ContentProcessChild()
@@ -30,33 +34,55 @@ ContentProcessChild::Init(MessageLoop* a
sSingleton = this;
return true;
}
IFrameEmbeddingProtocolChild*
ContentProcessChild::IFrameEmbeddingConstructor(const MagicWindowHandle& hwnd)
{
- return new TabChild(hwnd);
+ IFrameEmbeddingProtocolChild* iframe = new TabChild(hwnd);
+ if (iframe && mIFrames.AppendElement(iframe)) {
+ return iframe;
+ }
+ delete iframe;
+ return nsnull;
}
nsresult
ContentProcessChild::IFrameEmbeddingDestructor(IFrameEmbeddingProtocolChild* iframe)
{
- delete iframe;
+ mIFrames.RemoveElement(iframe);
return NS_OK;
}
TestShellProtocolChild*
ContentProcessChild::TestShellConstructor()
{
- return new TestShellChild();
+ TestShellProtocolChild* testshell = new TestShellChild();
+ if (testshell && mTestShells.AppendElement(testshell)) {
+ return testshell;
+ }
+ delete testshell;
+ return nsnull;
}
nsresult
ContentProcessChild::TestShellDestructor(TestShellProtocolChild* shell)
{
- delete shell;
- return NS_OK;
+ mTestShells.RemoveElement(shell);
+ return NS_OK;
+}
+
+void
+ContentProcessChild::Quit()
+{
+ mIFrames.Clear();
+ mTestShells.Clear();
+
+ nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID));
+ if (appStartup) {
+ appStartup->Quit(nsIAppStartup::eForceQuit);
+ }
}
} // namespace dom
} // namespace mozilla
--- a/dom/ipc/ContentProcessChild.h
+++ b/dom/ipc/ContentProcessChild.h
@@ -1,16 +1,19 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: sw=4 ts=4 et : */
#ifndef mozilla_dom_ContentProcessChild_h
#define mozilla_dom_ContentProcessChild_h
#include "mozilla/dom/ContentProcessProtocolChild.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+
namespace mozilla {
namespace dom {
class ContentProcessChild
: public ContentProcessProtocolChild
{
public:
ContentProcessChild();
@@ -21,21 +24,26 @@ public:
static ContentProcessChild* GetSingleton() {
NS_ASSERTION(sSingleton, "not initialized");
return sSingleton;
}
virtual IFrameEmbeddingProtocolChild* IFrameEmbeddingConstructor(const MagicWindowHandle& hwnd);
virtual nsresult IFrameEmbeddingDestructor(IFrameEmbeddingProtocolChild*);
- virtual TestShellProtocolChild* TestShellConstructor();
- virtual nsresult TestShellDestructor(TestShellProtocolChild*);
+ virtual TestShellProtocolChild* TestShellConstructor();
+ virtual nsresult TestShellDestructor(TestShellProtocolChild*);
+
+ void Quit();
private:
static ContentProcessChild* sSingleton;
+ nsTArray<nsAutoPtr<IFrameEmbeddingProtocolChild> > mIFrames;
+ nsTArray<nsAutoPtr<TestShellProtocolChild> > mTestShells;
+
DISALLOW_EVIL_CONSTRUCTORS(ContentProcessChild);
};
} // namespace dom
} // namespace mozilla
#endif
--- a/ipc/chromium/src/base/message_loop.h
+++ b/ipc/chromium/src/base/message_loop.h
@@ -21,16 +21,20 @@
#if defined(OS_WIN)
// We need this to declare base::MessagePumpWin::Dispatcher, which we should
// really just eliminate.
#include "base/message_pump_win.h"
#elif defined(OS_POSIX)
#include "base/message_pump_libevent.h"
#endif
+#ifdef CHROMIUM_MOZILLA_BUILD
+class DoWorkRunnable;
+#endif
+
// A MessageLoop is used to process events for a particular thread. There is
// at most one MessageLoop instance per thread.
//
// Events include at a minimum Task instances submitted to PostTask or those
// managed by TimerManager. Depending on the type of message pump used by the
// MessageLoop other events such as UI messages may be processed. On Windows
// APC calls (as time permits) and signals sent to a registered set of HANDLEs
// may also be processed.
@@ -52,17 +56,22 @@
// HRESULT hr = DoDragDrop(...); // Implicitly runs a modal message loop here.
// MessageLoop::current()->SetNestableTasksAllowed(old_state);
// // Process hr (the result returned by DoDragDrop().
//
// Please be SURE your task is reentrant (nestable) and all global variables
// are stable and accessible before calling SetNestableTasksAllowed(true).
//
class MessageLoop : public base::MessagePump::Delegate {
- public:
+
+#ifdef CHROMIUM_MOZILLA_BUILD
+ friend class DoWorkRunnable;
+#endif
+
+public:
static void EnableHistogrammer(bool enable_histogrammer);
// A DestructionObserver is notified when the current MessageLoop is being
// destroyed. These obsevers are notified prior to MessageLoop::current()
// being changed to return NULL. This gives interested parties the chance to
// do final cleanup that depends on the MessageLoop.
//
// NOTE: Any tasks posted to the MessageLoop during this notification will
--- a/ipc/glue/AsyncChannel.cpp
+++ b/ipc/glue/AsyncChannel.cpp
@@ -35,25 +35,36 @@
* 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 "mozilla/ipc/AsyncChannel.h"
#include "mozilla/ipc/GeckoThread.h"
+#include "mozilla/dom/ContentProcessChild.h"
+using mozilla::dom::ContentProcessChild;
+
#include "nsDebug.h"
+#include "nsXULAppAPI.h"
template<>
struct RunnableMethodTraits<mozilla::ipc::AsyncChannel>
{
static void RetainCallee(mozilla::ipc::AsyncChannel* obj) { }
static void ReleaseCallee(mozilla::ipc::AsyncChannel* obj) { }
};
+template<>
+struct RunnableMethodTraits<ContentProcessChild>
+{
+ static void RetainCallee(ContentProcessChild* obj) { }
+ static void ReleaseCallee(ContentProcessChild* obj) { }
+};
+
namespace mozilla {
namespace ipc {
bool
AsyncChannel::Open(Transport* aTransport, MessageLoop* aIOLoop)
{
NS_PRECONDITION(!mTransport, "Open() called > once");
NS_PRECONDITION(aTransport, "need transport layer");
@@ -138,36 +149,54 @@ AsyncChannel::OnDispatchMessage(const Me
//
// The methods below run in the context of the IO thread, and can proxy
// back to the methods above
//
void
AsyncChannel::OnMessageReceived(const Message& msg)
{
+ NS_ASSERTION(mChannelState != ChannelError, "Shouldn't get here!");
+
// wake up the worker, there's work to do
mWorkerLoop->PostTask(FROM_HERE,
NewRunnableMethod(this,
&AsyncChannel::OnDispatchMessage,
msg));
}
void
AsyncChannel::OnChannelConnected(int32 peer_pid)
{
mChannelState = ChannelConnected;
}
void
AsyncChannel::OnChannelError()
{
- NS_WARNING("Channel error, quitting IO loop!");
- // FIXME/cjones impl
mChannelState = ChannelError;
- MessageLoop::current()->Quit();
+
+ if (XRE_GetProcessType() == GeckoProcessType_Default) {
+ // Parent process, one of our children died. Notify?
+ }
+ else {
+ // Child process, initiate quit sequence.
+#ifdef DEBUG
+ // XXXbent this is totally out of place, but works for now.
+ mWorkerLoop->PostTask(FROM_HERE,
+ NewRunnableMethod(ContentProcessChild::GetSingleton(),
+ &ContentProcessChild::Quit));
+
+ // Must exit the IO loop, which will then join with the UI loop.
+ MessageLoop::current()->Quit();
+#else
+ // Go ahead and abort here.
+ NS_DebugBreak(NS_DEBUG_ABORT, nsnull, nsnull, nsnull, 0);
+#endif
+ }
}
void
AsyncChannel::OnChannelOpened()
{
mChannelState = ChannelOpening;
/*assert*/mTransport->Connect();
}
--- a/ipc/glue/MessagePump.cpp
+++ b/ipc/glue/MessagePump.cpp
@@ -64,20 +64,33 @@ TimerCallback(nsITimer* aTimer,
void* aClosure)
{
MessagePump* messagePump = reinterpret_cast<MessagePump*>(aClosure);
messagePump->ScheduleWork();
}
} /* anonymous namespace */
+class DoWorkRunnable : public nsRunnable
+{
+public:
+ NS_IMETHOD Run() {
+ MessageLoop* loop = MessageLoop::current();
+ NS_ASSERTION(loop, "Shouldn't be null!");
+ if (loop) {
+ loop->DoWork();
+ }
+ return NS_OK;
+ }
+};
+
MessagePump::MessagePump()
: mThread(nsnull)
{
- mDummyEvent = new nsRunnable();
+ mDummyEvent = new DoWorkRunnable();
// I'm tired of adding OOM checks.
NS_ADDREF(mDummyEvent);
}
MessagePump::~MessagePump()
{
NS_RELEASE(mDummyEvent);
}
--- a/ipc/testshell/TestShell.ipdl
+++ b/ipc/testshell/TestShell.ipdl
@@ -31,23 +31,30 @@
* 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 protocol "ContentProcess.ipdl";
+include protocol "TestShellCommand.ipdl";
namespace mozilla {
namespace ipc {
-sync protocol TestShell
+protocol TestShell
{
manager ContentProcess;
+ manages TestShellCommand;
+
child:
- async SendCommand(nsString aCommand);
- sync SendCommandWithResponse(nsString aCommand) returns (nsString aResponse);
+ ExecuteCommand(nsString aCommand);
+
+ TestShellCommand(nsString aCommand);
+
+parent:
+ ~TestShellCommand(nsString aResponse);
};
} // namespace ipc
} // namespace mozilla
--- a/ipc/testshell/TestShellChild.cpp
+++ b/ipc/testshell/TestShellChild.cpp
@@ -34,45 +34,79 @@
*
* ***** END LICENSE BLOCK ***** */
#include "TestShellChild.h"
#include "XPCShellEnvironment.h"
using mozilla::ipc::TestShellChild;
+using mozilla::ipc::TestShellCommandProtocolChild;
using mozilla::ipc::XPCShellEnvironment;
TestShellChild::TestShellChild()
- : mXPCShell(XPCShellEnvironment::CreateEnvironment())
+: mXPCShell(nsnull)
{
-
+ XPCShellEnvironment* env = XPCShellEnvironment::CreateEnvironment();
+ if (env) {
+ if (env->DefineIPCCommands(this)) {
+ mXPCShell = env;
+ }
+ else {
+ XPCShellEnvironment::DestroyEnvironment(env);
+ }
+ }
}
TestShellChild::~TestShellChild()
{
-
+ if (mXPCShell) {
+ XPCShellEnvironment::DestroyEnvironment(mXPCShell);
+ }
}
nsresult
-TestShellChild::RecvSendCommand(const nsString& aCommand)
+TestShellChild::RecvExecuteCommand(const nsString& aCommand)
{
if (mXPCShell->IsQuitting()) {
NS_WARNING("Commands sent after quit command issued!");
return NS_ERROR_UNEXPECTED;
}
return mXPCShell->EvaluateString(aCommand) ? NS_OK : NS_ERROR_FAILURE;
}
+TestShellCommandProtocolChild*
+TestShellChild::TestShellCommandConstructor(const nsString& aCommand)
+{
+ return new TestShellCommandProtocolChild();
+}
+
nsresult
-TestShellChild::RecvSendCommandWithResponse(const nsString& aCommand,
- nsString* aResponse)
+TestShellChild::TestShellCommandDestructor(TestShellCommandProtocolChild* aCommand,
+ const nsString& aResponse)
{
+ NS_ENSURE_ARG_POINTER(aCommand);
+ delete aCommand;
+ return NS_OK;
+}
+
+nsresult
+TestShellChild::RecvTestShellCommandConstructor(TestShellCommandProtocolChild* aActor,
+ const nsString& aCommand)
+{
+ NS_ASSERTION(aActor, "Shouldn't be null!");
+
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;
+ nsString response;
+ if (!mXPCShell->EvaluateString(aCommand, &response)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv = SendTestShellCommandDestructor(aActor, response);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
}
--- a/ipc/testshell/TestShellChild.h
+++ b/ipc/testshell/TestShellChild.h
@@ -33,31 +33,42 @@
* 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"
+#include "mozilla/ipc/TestShellCommandProtocolChild.h"
namespace mozilla {
namespace ipc {
class XPCShellEnvironment;
class TestShellChild : public TestShellProtocolChild
{
public:
TestShellChild();
- virtual ~TestShellChild();
+ ~TestShellChild();
+
+ nsresult
+ RecvExecuteCommand(const nsString& aCommand);
+
+ TestShellCommandProtocolChild*
+ TestShellCommandConstructor(const nsString& aCommand);
- virtual nsresult RecvSendCommand(const nsString& aCommand);
- virtual nsresult RecvSendCommandWithResponse(const nsString& aCommand,
- nsString* aResponse);
+ nsresult
+ RecvTestShellCommandConstructor(TestShellCommandProtocolChild* aActor,
+ const nsString& aCommand);
+
+ nsresult
+ TestShellCommandDestructor(TestShellCommandProtocolChild* aCommand,
+ const nsString& aResponse);
void SetXPCShell(XPCShellEnvironment* aXPCShell) {
mXPCShell = aXPCShell;
}
private:
XPCShellEnvironment* mXPCShell;
};
new file mode 100644
--- /dev/null
+++ b/ipc/testshell/TestShellCommand.ipdl
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* ***** 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 protocol "TestShell.ipdl";
+
+namespace mozilla {
+namespace ipc {
+
+protocol TestShellCommand
+{
+ manager TestShell;
+};
+
+} // namespace ipc
+} // namespace mozilla
--- a/ipc/testshell/TestShellParent.cpp
+++ b/ipc/testshell/TestShellParent.cpp
@@ -31,19 +31,101 @@
* 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"
+#include "nsAutoPtr.h"
+#include "XPCShellEnvironment.h"
+
using mozilla::ipc::TestShellParent;
+using mozilla::ipc::TestShellCommandParent;
+using mozilla::ipc::TestShellCommandProtocolParent;
+using mozilla::ipc::XPCShellEnvironment;
-TestShellParent::TestShellParent()
+TestShellCommandProtocolParent*
+TestShellParent::TestShellCommandConstructor(const nsString& aCommand)
{
+ return new TestShellCommandParent();
+}
+nsresult
+TestShellParent::TestShellCommandDestructor(TestShellCommandProtocolParent* aActor,
+ const nsString& aResponse)
+{
+ NS_ENSURE_ARG_POINTER(aActor);
+ delete aActor;
+ return NS_OK;
}
-TestShellParent::~TestShellParent()
+nsresult
+TestShellParent::RecvTestShellCommandDestructor(TestShellCommandProtocolParent* aActor,
+ const nsString& aResponse)
+{
+ NS_ENSURE_ARG_POINTER(aActor);
+
+ TestShellCommandParent* command =
+ static_cast<TestShellCommandParent*>(aActor);
+
+ JSBool ok = command->RunCallback(aResponse);
+ command->ReleaseCallback();
+
+ if (!mXPCShell) {
+ NS_WARNING("Processing a child message after exiting, need to spin events "
+ "somehow to process this result");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ NS_WARN_IF_FALSE(mXPCShell->EventLoopDepth(), "EventLoopDepth mismatch!");
+ if (mXPCShell->EventLoopDepth()) {
+ mXPCShell->DecrementEventLoopDepth();
+ }
+
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+ return NS_OK;
+}
+
+JSBool
+TestShellCommandParent::SetCallback(JSContext* aCx,
+ jsval aCallback)
{
+ if (!mCallback.Hold(aCx)) {
+ return JS_FALSE;
+ }
+ mCallback = aCallback;
+ mCx = aCx;
+
+ return JS_TRUE;
}
+
+JSBool
+TestShellCommandParent::RunCallback(const nsString& aResponse)
+{
+ NS_ENSURE_TRUE(mCallback && mCx, JS_FALSE);
+
+ JSAutoRequest ar(mCx);
+
+ JSObject* global = JS_GetGlobalObject(mCx);
+ NS_ENSURE_TRUE(global, JS_FALSE);
+
+ JSString* str = JS_NewUCStringCopyN(mCx, aResponse.get(), aResponse.Length());
+ NS_ENSURE_TRUE(str, JS_FALSE);
+
+ jsval argv[] = { STRING_TO_JSVAL(str) };
+ int argc = NS_ARRAY_LENGTH(argv);
+
+ jsval rval;
+ JSBool ok = JS_CallFunctionValue(mCx, global, mCallback, argc, argv, &rval);
+ NS_ENSURE_TRUE(ok, JS_FALSE);
+
+ return JS_TRUE;
+}
+
+void
+TestShellCommandParent::ReleaseCallback()
+{
+ mCallback.Release();
+}
--- a/ipc/testshell/TestShellParent.h
+++ b/ipc/testshell/TestShellParent.h
@@ -33,23 +33,65 @@
* 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"
+#include "mozilla/ipc/TestShellCommandProtocolParent.h"
+
+#include "jsapi.h"
+#include "nsAutoJSValHolder.h"
+#include "nsStringGlue.h"
namespace mozilla {
namespace ipc {
+class XPCShellEnvironment;
+
+class TestShellCommandParent : public TestShellCommandProtocolParent
+{
+public:
+ TestShellCommandParent() : mCx(NULL) { }
+
+ JSBool SetCallback(JSContext* aCx,
+ jsval aCallback);
+
+ JSBool RunCallback(const nsString& aResponse);
+
+ void ReleaseCallback();
+
+private:
+ JSContext* mCx;
+ nsAutoJSValHolder mCallback;
+};
+
class TestShellParent : public TestShellProtocolParent
{
public:
- TestShellParent();
- ~TestShellParent();
+ TestShellParent() : mXPCShell(nsnull) { }
+
+ void
+ SetXPCShell(XPCShellEnvironment* aXPCShell) {
+ mXPCShell = aXPCShell;
+ }
+
+ TestShellCommandProtocolParent*
+ TestShellCommandConstructor(const nsString& aCommand);
+
+ nsresult
+ TestShellCommandDestructor(TestShellCommandProtocolParent* aActor,
+ const nsString& aResponse);
+
+ nsresult
+ RecvTestShellCommandDestructor(TestShellCommandProtocolParent* aActor,
+ const nsString& aResponse);
+
+private:
+ XPCShellEnvironment* mXPCShell;
};
} /* namespace ipc */
} /* namespace mozilla */
#endif /* _IPC_TESTSHELL_TESTSHELLPARENT_H_ */
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -59,31 +59,33 @@
#include "nsIJSRuntimeService.h"
#include "nsIPrincipal.h"
#include "nsIScriptSecurityManager.h"
#include "nsIURI.h"
#include "nsIXPConnect.h"
#include "nsIXPCScriptable.h"
#include "nsJSUtils.h"
+#include "nsThreadUtils.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;
+using mozilla::ipc::TestShellCommandProtocolParent;
namespace {
-static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js";
+static const char kDefaultRuntimeScriptFilename[] = "ipcshell.js";
class FullTrustSecMan : public nsIScriptSecurityManager
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCSECURITYMANAGER
NS_DECL_NSISCRIPTSECURITYMANAGER
@@ -769,16 +771,17 @@ PrintUsage(void)
static void
ProcessFile(JSContext *cx,
JSObject *obj,
const char *filename,
FILE *file,
JSBool forceTTY)
{
XPCShellEnvironment* env = Environment(cx);
+ XPCShellEnvironment::AutoContextPusher pusher(env);
JSScript *script;
jsval result;
int lineno, startline;
JSBool ok, hitEOF;
char *bufp, buffer[4096];
JSString *str;
@@ -870,16 +873,96 @@ ProcessFile(JSContext *cx,
}
JS_DestroyScript(cx, script);
}
} while (!hitEOF && !env->IsQuitting());
fprintf(stdout, "\n");
}
+static JSBool
+SendCommand(JSContext *cx,
+ JSObject *obj,
+ uintN argc,
+ jsval *argv,
+ jsval *rval)
+{
+ if (argc == 0) {
+ JS_ReportError(cx, "Function takes at least one argument!");
+ return JS_FALSE;
+ }
+
+ JSString* str = JS_ValueToString(cx, argv[0]);
+ if (!str) {
+ JS_ReportError(cx, "Could not convert argument 1 to string!");
+ return JS_FALSE;
+ }
+
+ nsDependentJSString command(str);
+ JSBool ok;
+
+ if (argc > 1) {
+ if (JS_TypeOfValue(cx, argv[1]) != JSTYPE_FUNCTION) {
+ JS_ReportError(cx, "Could not convert argument 2 to function!");
+ return JS_FALSE;
+ }
+ ok = Environment(cx)->DoSendCommand(command, cx, argv[1]);
+ if (ok) {
+ Environment(cx)->IncrementEventLoopDepth();
+ }
+ }
+ else {
+ ok = Environment(cx)->DoSendCommand(command);
+ }
+
+ if (!ok) {
+ JS_ReportError(cx, "Failed to send command!");
+ return JS_FALSE;
+ }
+
+ return JS_TRUE;
+}
+
+static JSBool
+RunEventLoop(JSContext *cx,
+ JSObject *obj,
+ uintN argc,
+ jsval *argv,
+ jsval *rval)
+{
+ NS_ASSERTION(Environment(cx)->EventLoopDepth() >= 0, "Bad depth!");
+ Environment(cx)->IncrementEventLoopDepth();
+ return JS_TRUE;
+}
+
+static JSBool
+StopEventLoop(JSContext *cx,
+ JSObject *obj,
+ uintN argc,
+ jsval *argv,
+ jsval *rval)
+{
+ XPCShellEnvironment* env = Environment(cx);
+ if (env->EventLoopDepth() < 1) {
+ JS_ReportError(cx, "Mismatched call to DecrementEventLoopDepth");
+ return JS_FALSE;
+ }
+
+ env->DecrementEventLoopDepth();
+ return JS_TRUE;
+}
+
+JSFunctionSpec gParentFunctions[] =
+{
+ {"sendCommand", SendCommand, 1, 0, 0},
+ {"runEventLoop", RunEventLoop, 0, 0, 0},
+ {"stopEventLoop", StopEventLoop, 0, 0, 0},
+ {nsnull, nsnull, 0, 0, 0}
+};
+
} /* 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
@@ -1174,16 +1257,36 @@ XPCShellDirProvider::GetFile(const char
*persistent = PR_TRUE;
NS_ADDREF(*result = mGREDir);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
+XPCShellEnvironment::
+AutoContextPusher::AutoContextPusher(XPCShellEnvironment* aEnv)
+{
+ NS_ASSERTION(aEnv->mCx, "Null context?!");
+
+ if (NS_SUCCEEDED(aEnv->mCxStack->Push(aEnv->mCx))) {
+ mEnv = aEnv;
+ }
+}
+
+XPCShellEnvironment::
+AutoContextPusher::~AutoContextPusher()
+{
+ if (mEnv) {
+ JSContext* cx;
+ mEnv->mCxStack->Pop(&cx);
+ NS_ASSERTION(cx == mEnv->mCx, "Wrong context on the stack!");
+ }
+}
+
// static
XPCShellEnvironment*
XPCShellEnvironment::CreateEnvironment()
{
XPCShellEnvironment* env = new XPCShellEnvironment();
if (env && !env->Init()) {
delete env;
env = nsnull;
@@ -1197,16 +1300,17 @@ XPCShellEnvironment::DestroyEnvironment(
{
delete aEnv;
}
XPCShellEnvironment::XPCShellEnvironment()
: mCx(NULL),
mJSPrincipals(NULL),
mExitCode(0),
+ mEventLoopDepth(0),
mQuitting(JS_FALSE),
mReportWarnings(JS_TRUE),
mCompileOnly(JS_FALSE),
mParent(nsnull)
{
}
XPCShellEnvironment::~XPCShellEnvironment()
@@ -1217,23 +1321,17 @@ XPCShellEnvironment::~XPCShellEnvironmen
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);
- }
+ mCxStack = nsnull;
if (mJSPrincipals) {
JSPRINCIPALS_DROP(mCx, mJSPrincipals);
}
JSRuntime* rt = gOldContextCallback ? JS_GetRuntime(mCx) : NULL;
JS_DestroyContext(mCx);
@@ -1321,22 +1419,19 @@ XPCShellEnvironment::Init()
}
nsCOMPtr<nsIJSContextStack> cxStack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
if (!cxStack) {
NS_ERROR("failed to get the nsThreadJSContextStack service!");
return false;
}
+ mCxStack = cxStack;
- if(NS_FAILED(cxStack->Push(cx))) {
- NS_ERROR("failed to push the current JSContext on the nsThreadJSContextStack!");
- return false;
- }
- mCxStack = cxStack;
+ AutoContextPusher pusher(this);
nsCOMPtr<nsIXPCScriptable> backstagePass;
rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass));
if (NS_FAILED(rv)) {
NS_ERROR("Failed to get backstage pass from rtsvc!");
return false;
}
@@ -1387,106 +1482,49 @@ XPCShellEnvironment::Init()
runtimeScriptFile, JS_FALSE);
fclose(runtimeScriptFile);
}
return true;
}
void
-XPCShellEnvironment::Process(const char* aFilename,
- JSBool aIsInteractive)
+XPCShellEnvironment::Process(const char* aFilename)
{
NS_ASSERTION(GetGlobalObject(), "Should never be null!");
FILE* file;
- if (!aFilename || aIsInteractive) {
+ if (!aFilename) {
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)
+ ProcessFile(mCx, GetGlobalObject(), aFilename, file, !aFilename);
+ 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;
- }
-
- nsDependentJSString command(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;
- }
+ if (EventLoopDepth()) {
+ nsCOMPtr<nsIThread> currentThread;
+ NS_GetCurrentThread(getter_AddRefs(currentThread));
- nsDependentJSString command(str);
- nsAutoString result;
- if (!Environment(cx)->DoSendCommand(command, &result)) {
- JS_ReportError(cx, "Failed to send command!");
- return JS_FALSE;
- }
-
- JSString* resultStr = JS_NewUCStringCopyN(cx, result.get(), result.Length());
- if (!resultStr) {
- JS_ReportError(cx, "Failed to convert response to string!");
- return JS_FALSE;
- }
-
- *rval = STRING_TO_JSVAL(resultStr);
- return JS_TRUE;
+ while (EventLoopDepth()) {
+ NS_ProcessNextEvent(currentThread, PR_TRUE);
+ }
+ }
}
-} /* anonymous namespace */
-
bool
XPCShellEnvironment::DefineIPCCommands(TestShellChild* aChild)
{
NS_ASSERTION(aChild, "Don't hand me null!");
// XXXbent Nothing here yet, soon though!
return true;
}
@@ -1497,42 +1535,45 @@ XPCShellEnvironment::DefineIPCCommands(T
NS_ASSERTION(aParent, "Don't hand me null!");
mParent = aParent;
JSObject* global = GetGlobalObject();
JSAutoRequest ar(mCx);
- 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;
+ if (!JS_DefineFunctions(mCx, global, gParentFunctions)) {
+ NS_ERROR("JS_DefineFunctions failed!");
+ return false;
}
return true;
}
JSBool
XPCShellEnvironment::DoSendCommand(const nsString& aCommand,
- nsString* aResult)
+ JSContext* aCx,
+ jsval aCallback)
{
- nsresult rv = aResult ?
- mParent->SendSendCommandWithResponse(aCommand, aResult) :
- mParent->SendSendCommand(aCommand);
+ if (aCx) {
+ TestShellCommandParent* command = static_cast<TestShellCommandParent*>(
+ mParent->SendTestShellCommandConstructor(aCommand));
+ NS_ENSURE_TRUE(command, JS_FALSE);
- return NS_SUCCEEDED(rv) ? JS_TRUE : JS_FALSE;
+ if (!command->SetCallback(aCx, aCallback)) {
+ NS_WARNING("Failed to set callback!");
+ return JS_FALSE;
+ }
+ }
+ else {
+ nsresult rv = mParent->SendExecuteCommand(aCommand);
+ NS_ENSURE_SUCCESS(rv, JS_FALSE);
+ }
+
+ return JS_TRUE;
}
bool
XPCShellEnvironment::EvaluateString(const nsString& aString,
nsString* aResult)
{
JSAutoRequest ar(mCx);
--- a/ipc/testshell/XPCShellEnvironment.h
+++ b/ipc/testshell/XPCShellEnvironment.h
@@ -60,24 +60,24 @@ 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);
+ void Process(const char* aFilename = nsnull);
bool DefineIPCCommands(TestShellChild* aChild);
bool DefineIPCCommands(TestShellParent* aParent);
JSBool DoSendCommand(const nsString& aCommand,
- nsString* aResult = nsnull);
+ JSContext* aCx = nsnull,
+ jsval aCallback = JSVAL_VOID);
bool EvaluateString(const nsString& aString,
nsString* aResult = nsnull);
JSPrincipals* GetPrincipal() {
return mJSPrincipals;
}
@@ -108,29 +108,49 @@ public:
void SetShouldCompoleOnly(JSBool aCompileOnly) {
mCompileOnly = aCompileOnly;
}
JSBool ShouldCompileOnly() {
return mCompileOnly;
}
+ int EventLoopDepth() {
+ return mEventLoopDepth;
+ }
+ void IncrementEventLoopDepth() {
+ ++mEventLoopDepth;
+ }
+ void DecrementEventLoopDepth() {
+ --mEventLoopDepth;
+ }
+
+ class AutoContextPusher
+ {
+ public:
+ AutoContextPusher(XPCShellEnvironment* aEnv);
+ ~AutoContextPusher();
+ private:
+ XPCShellEnvironment* mEnv;
+ };
+
protected:
XPCShellEnvironment();
~XPCShellEnvironment();
bool Init();
private:
JSContext* mCx;
nsAutoJSValHolder mGlobalHolder;
nsCOMPtr<nsIJSContextStack> mCxStack;
JSPrincipals* mJSPrincipals;
int mExitCode;
+ int mEventLoopDepth;
JSBool mQuitting;
JSBool mReportWarnings;
JSBool mCompileOnly;
TestShellParent* mParent;
};
} /* namespace ipc */
--- a/ipc/testshell/ipdl.mk
+++ b/ipc/testshell/ipdl.mk
@@ -1,3 +1,4 @@
IPDLSRCS = \
TestShell.ipdl \
+ TestShellCommand.ipdl \
$(NULL)
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -332,18 +332,18 @@ XRE_InitParentProcess(int aArgc,
void* aMainFunctionData)
{
NS_ENSURE_ARG_MIN(aArgc, 1);
NS_ENSURE_ARG_POINTER(aArgv);
NS_ENSURE_ARG_POINTER(aArgv[0]);
base::AtExitManager exitManager;
CommandLine::Init(aArgc, aArgv);
+ MessageLoopForUI mainMessageLoop;
ScopedXREEmbed embed;
- MessageLoopForUI mainMessageLoop;
{
// Make chromium's IPC thread
#if defined(OS_LINUX)
// The lifetime of the BACKGROUND_X11 thread is a subset of the IO thread so
// we start it now.
scoped_ptr<base::Thread> x11Thread(
new BrowserProcessSubThread(BrowserProcessSubThread::BACKGROUND_X11));
@@ -417,26 +417,31 @@ TestShellMain(int argc, char** argv)
nsresult rv = NS_DispatchToCurrentThread(quitRunnable);
NS_ENSURE_SUCCESS(rv, 1);
nsAutoRef<XPCShellEnvironment> env(XPCShellEnvironment::CreateEnvironment());
NS_ENSURE_TRUE(env, 1);
ContentProcessParent* childProcess = ContentProcessParent::GetSingleton();
- if (!childProcess)
- return 1;
+ NS_ENSURE_TRUE(childProcess, 1);
TestShellParent* testShellParent = childProcess->CreateTestShell();
+ NS_ENSURE_TRUE(testShellParent, 1);
- env->DefineIPCCommands(testShellParent);
+ testShellParent->SetXPCShell(env);
+
+ bool ok = env->DefineIPCCommands(testShellParent);
+ NS_ENSURE_TRUE(ok, 1);
const char* filename = argc > 1 ? argv[1] : nsnull;
env->Process(filename);
+ testShellParent->SetXPCShell(nsnull);
+
return env->ExitCode();
}
struct TestShellData {
int* result;
int argc;
char** argv;
};