summaryrefslogtreecommitdiffstats
path: root/ipc/testshell
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/testshell')
-rw-r--r--ipc/testshell/PTestShell.ipdl27
-rw-r--r--ipc/testshell/PTestShellCommand.ipdl20
-rw-r--r--ipc/testshell/TestShellChild.cpp56
-rw-r--r--ipc/testshell/TestShellChild.h45
-rw-r--r--ipc/testshell/TestShellParent.cpp109
-rw-r--r--ipc/testshell/TestShellParent.h68
-rw-r--r--ipc/testshell/XPCShellEnvironment.cpp541
-rw-r--r--ipc/testshell/XPCShellEnvironment.h64
-rw-r--r--ipc/testshell/moz.build37
-rw-r--r--ipc/testshell/tests/test_ipcshell.js28
-rw-r--r--ipc/testshell/tests/test_ipcshell_child.js9
-rw-r--r--ipc/testshell/tests/xpcshell.ini9
12 files changed, 1013 insertions, 0 deletions
diff --git a/ipc/testshell/PTestShell.ipdl b/ipc/testshell/PTestShell.ipdl
new file mode 100644
index 000000000..db4d7eedc
--- /dev/null
+++ b/ipc/testshell/PTestShell.ipdl
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+include protocol PTestShellCommand;
+
+namespace mozilla {
+namespace ipc {
+
+async protocol PTestShell
+{
+ manager PContent;
+
+ manages PTestShellCommand;
+
+child:
+ async __delete__();
+
+ async ExecuteCommand(nsString aCommand);
+
+ async PTestShellCommand(nsString aCommand);
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/testshell/PTestShellCommand.ipdl b/ipc/testshell/PTestShellCommand.ipdl
new file mode 100644
index 000000000..fb6efd99f
--- /dev/null
+++ b/ipc/testshell/PTestShellCommand.ipdl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PTestShell;
+
+namespace mozilla {
+namespace ipc {
+
+protocol PTestShellCommand
+{
+ manager PTestShell;
+
+parent:
+ async __delete__(nsString aResponse);
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/testshell/TestShellChild.cpp b/ipc/testshell/TestShellChild.cpp
new file mode 100644
index 000000000..adb60e088
--- /dev/null
+++ b/ipc/testshell/TestShellChild.cpp
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestShellChild.h"
+
+using mozilla::ipc::TestShellChild;
+using mozilla::ipc::PTestShellCommandChild;
+using mozilla::ipc::XPCShellEnvironment;
+
+TestShellChild::TestShellChild()
+: mXPCShell(XPCShellEnvironment::CreateEnvironment())
+{
+}
+
+bool
+TestShellChild::RecvExecuteCommand(const nsString& aCommand)
+{
+ if (mXPCShell->IsQuitting()) {
+ NS_WARNING("Commands sent after quit command issued!");
+ return false;
+ }
+
+ return mXPCShell->EvaluateString(aCommand);
+}
+
+PTestShellCommandChild*
+TestShellChild::AllocPTestShellCommandChild(const nsString& aCommand)
+{
+ return new PTestShellCommandChild();
+}
+
+bool
+TestShellChild::DeallocPTestShellCommandChild(PTestShellCommandChild* aCommand)
+{
+ delete aCommand;
+ return true;
+}
+
+bool
+TestShellChild::RecvPTestShellCommandConstructor(PTestShellCommandChild* aActor,
+ const nsString& aCommand)
+{
+ if (mXPCShell->IsQuitting()) {
+ NS_WARNING("Commands sent after quit command issued!");
+ return false;
+ }
+
+ nsString response;
+ if (!mXPCShell->EvaluateString(aCommand, &response)) {
+ return false;
+ }
+
+ return PTestShellCommandChild::Send__delete__(aActor, response);
+}
+
diff --git a/ipc/testshell/TestShellChild.h b/ipc/testshell/TestShellChild.h
new file mode 100644
index 000000000..fcd1301eb
--- /dev/null
+++ b/ipc/testshell/TestShellChild.h
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ipc_testshell_TestShellChild_h
+#define ipc_testshell_TestShellChild_h 1
+
+#include "mozilla/ipc/PTestShellChild.h"
+#include "mozilla/ipc/PTestShellCommandChild.h"
+#include "mozilla/ipc/XPCShellEnvironment.h"
+
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+
+namespace ipc {
+
+class XPCShellEnvironment;
+
+class TestShellChild : public PTestShellChild
+{
+public:
+ TestShellChild();
+
+ bool
+ RecvExecuteCommand(const nsString& aCommand);
+
+ PTestShellCommandChild*
+ AllocPTestShellCommandChild(const nsString& aCommand);
+
+ bool
+ RecvPTestShellCommandConstructor(PTestShellCommandChild* aActor,
+ const nsString& aCommand);
+
+ bool
+ DeallocPTestShellCommandChild(PTestShellCommandChild* aCommand);
+
+private:
+ nsAutoPtr<XPCShellEnvironment> mXPCShell;
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* ipc_testshell_TestShellChild_h */
diff --git a/ipc/testshell/TestShellParent.cpp b/ipc/testshell/TestShellParent.cpp
new file mode 100644
index 000000000..f0c851101
--- /dev/null
+++ b/ipc/testshell/TestShellParent.cpp
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TestShellParent.h"
+
+/* This must occur *after* TestShellParent.h to avoid typedefs conflicts. */
+#include "jsfriendapi.h"
+#include "mozilla/ArrayUtils.h"
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+#include "xpcpublic.h"
+
+using namespace mozilla;
+using mozilla::ipc::TestShellParent;
+using mozilla::ipc::TestShellCommandParent;
+using mozilla::ipc::PTestShellCommandParent;
+using mozilla::dom::ContentParent;
+
+void
+TestShellParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005177
+}
+
+PTestShellCommandParent*
+TestShellParent::AllocPTestShellCommandParent(const nsString& aCommand)
+{
+ return new TestShellCommandParent();
+}
+
+bool
+TestShellParent::DeallocPTestShellCommandParent(PTestShellCommandParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+bool
+TestShellParent::CommandDone(TestShellCommandParent* command,
+ const nsString& aResponse)
+{
+ // XXX what should happen if the callback fails?
+ /*bool ok = */command->RunCallback(aResponse);
+ command->ReleaseCallback();
+
+ return true;
+}
+
+bool
+TestShellCommandParent::SetCallback(JSContext* aCx,
+ const JS::Value& aCallback)
+{
+ if (!mCallback.initialized()) {
+ mCallback.init(aCx, aCallback);
+ return true;
+ }
+
+ mCallback = aCallback;
+
+ return true;
+}
+
+bool
+TestShellCommandParent::RunCallback(const nsString& aResponse)
+{
+ NS_ENSURE_TRUE(mCallback.isObject(), false);
+
+ // We're about to run script via JS_CallFunctionValue, so we need an
+ // AutoEntryScript. This is just for testing and not in any spec.
+ dom::AutoEntryScript aes(&mCallback.toObject(), "TestShellCommand");
+ JSContext* cx = aes.cx();
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+
+ JSString* str = JS_NewUCStringCopyN(cx, aResponse.get(), aResponse.Length());
+ NS_ENSURE_TRUE(str, false);
+
+ JS::Rooted<JS::Value> strVal(cx, JS::StringValue(str));
+
+ JS::Rooted<JS::Value> rval(cx);
+ JS::Rooted<JS::Value> callback(cx, mCallback);
+ bool ok = JS_CallFunctionValue(cx, global, callback, JS::HandleValueArray(strVal), &rval);
+ NS_ENSURE_TRUE(ok, false);
+
+ return true;
+}
+
+void
+TestShellCommandParent::ReleaseCallback()
+{
+ mCallback.reset();
+}
+
+bool
+TestShellCommandParent::ExecuteCallback(const nsString& aResponse)
+{
+ return static_cast<TestShellParent*>(Manager())->CommandDone(
+ this, aResponse);
+}
+
+void
+TestShellCommandParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (why == AbnormalShutdown) {
+ ExecuteCallback(EmptyString());
+ }
+}
diff --git a/ipc/testshell/TestShellParent.h b/ipc/testshell/TestShellParent.h
new file mode 100644
index 000000000..34190bda3
--- /dev/null
+++ b/ipc/testshell/TestShellParent.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ipc_testshell_TestShellParent_h
+#define ipc_testshell_TestShellParent_h 1
+
+#include "mozilla/ipc/PTestShellParent.h"
+#include "mozilla/ipc/PTestShellCommandParent.h"
+
+#include "js/TypeDecls.h"
+#include "js/RootingAPI.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+namespace ipc {
+
+class TestShellCommandParent;
+
+class TestShellParent : public PTestShellParent
+{
+public:
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ PTestShellCommandParent*
+ AllocPTestShellCommandParent(const nsString& aCommand) override;
+
+ bool
+ DeallocPTestShellCommandParent(PTestShellCommandParent* aActor) override;
+
+ bool
+ CommandDone(TestShellCommandParent* aActor, const nsString& aResponse);
+};
+
+
+class TestShellCommandParent : public PTestShellCommandParent
+{
+public:
+ TestShellCommandParent() {}
+
+ bool SetCallback(JSContext* aCx, const JS::Value& aCallback);
+
+ bool RunCallback(const nsString& aResponse);
+
+ void ReleaseCallback();
+
+protected:
+ bool ExecuteCallback(const nsString& aResponse);
+
+ void ActorDestroy(ActorDestroyReason why);
+
+ bool Recv__delete__(const nsString& aResponse) {
+ return ExecuteCallback(aResponse);
+ }
+
+private:
+ JS::PersistentRooted<JS::Value> mCallback;
+};
+
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* ipc_testshell_TestShellParent_h */
diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp
new file mode 100644
index 000000000..a6979ccae
--- /dev/null
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -0,0 +1,541 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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
+
+#include "base/basictypes.h"
+
+#include "jsapi.h"
+
+#include "xpcpublic.h"
+
+#include "XPCShellEnvironment.h"
+
+#include "mozilla/XPCOM.h"
+
+#include "nsIChannel.h"
+#include "nsIClassInfo.h"
+#include "nsIDirectoryService.h"
+#include "nsIPrincipal.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIURI.h"
+#include "nsIXPConnect.h"
+#include "nsIXPCScriptable.h"
+
+#include "nsJSUtils.h"
+#include "nsJSPrincipals.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#include "BackstagePass.h"
+
+#include "TestShellChild.h"
+#include "TestShellParent.h"
+
+using mozilla::ipc::XPCShellEnvironment;
+using mozilla::ipc::TestShellChild;
+using mozilla::ipc::TestShellParent;
+using mozilla::AutoSafeJSContext;
+using mozilla::dom::AutoJSAPI;
+using mozilla::dom::AutoEntryScript;
+using namespace JS;
+
+namespace {
+
+static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js";
+
+inline XPCShellEnvironment*
+Environment(Handle<JSObject*> global)
+{
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(global)) {
+ return nullptr;
+ }
+ JSContext* cx = jsapi.cx();
+ Rooted<Value> v(cx);
+ if (!JS_GetProperty(cx, global, "__XPCShellEnvironment", &v) ||
+ !v.get().isDouble())
+ {
+ return nullptr;
+ }
+ return static_cast<XPCShellEnvironment*>(v.get().toPrivate());
+}
+
+static bool
+Print(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ for (unsigned i = 0; i < args.length(); i++) {
+ JSString *str = JS::ToString(cx, args[i]);
+ if (!str)
+ return false;
+ JSAutoByteString bytes(cx, str);
+ if (!bytes)
+ return false;
+ fprintf(stdout, "%s%s", i ? " " : "", bytes.ptr());
+ fflush(stdout);
+ }
+ fputc('\n', stdout);
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+GetLine(char *bufp,
+ FILE *file,
+ const char *prompt)
+{
+ char line[256];
+ fputs(prompt, stdout);
+ fflush(stdout);
+ if (!fgets(line, sizeof line, file))
+ return false;
+ strcpy(bufp, line);
+ return true;
+}
+
+static bool
+Dump(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ if (!args.length())
+ return true;
+
+ JSString *str = JS::ToString(cx, args[0]);
+ if (!str)
+ return false;
+ JSAutoByteString bytes(cx, str);
+ if (!bytes)
+ return false;
+
+ fputs(bytes.ptr(), stdout);
+ fflush(stdout);
+ return true;
+}
+
+static bool
+Load(JSContext *cx,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+
+ if (!JS_IsGlobalObject(obj)) {
+ JS_ReportErrorASCII(cx, "Trying to load() into a non-global object");
+ return false;
+ }
+
+ for (unsigned i = 0; i < args.length(); i++) {
+ JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[i]));
+ if (!str)
+ return false;
+ JSAutoByteString filename(cx, str);
+ if (!filename)
+ return false;
+ FILE *file = fopen(filename.ptr(), "r");
+ if (!file) {
+ filename.clear();
+ if (!filename.encodeUtf8(cx, str))
+ return false;
+ JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading", filename.ptr());
+ return false;
+ }
+ JS::CompileOptions options(cx);
+ options.setUTF8(true)
+ .setFileAndLine(filename.ptr(), 1);
+ JS::Rooted<JSScript*> script(cx);
+ bool ok = JS::Compile(cx, options, file, &script);
+ fclose(file);
+ if (!ok)
+ return false;
+
+ if (!JS_ExecuteScript(cx, script)) {
+ return false;
+ }
+ }
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+Version(JSContext *cx,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ args.rval().setInt32(JS_GetVersion(cx));
+ if (args.get(0).isInt32())
+ JS_SetVersionForCompartment(js::GetContextCompartment(cx),
+ JSVersion(args[0].toInt32()));
+ return true;
+}
+
+static bool
+Quit(JSContext *cx,
+ unsigned argc,
+ JS::Value *vp)
+{
+ Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ XPCShellEnvironment* env = Environment(global);
+ env->SetIsQuitting();
+
+ return false;
+}
+
+static bool
+DumpXPC(JSContext *cx,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs args = CallArgsFromVp(argc, vp);
+
+ uint16_t depth = 2;
+ if (args.length() > 0) {
+ if (!JS::ToUint16(cx, args[0], &depth))
+ return false;
+ }
+
+ nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+ if (xpc)
+ xpc->DebugDump(int16_t(depth));
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+GC(JSContext *cx,
+ unsigned argc,
+ JS::Value *vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ JS_GC(cx);
+
+ args.rval().setUndefined();
+ return true;
+}
+
+#ifdef JS_GC_ZEAL
+static bool
+GCZeal(JSContext *cx, unsigned argc, JS::Value *vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ uint32_t zeal;
+ if (!ToUint32(cx, args.get(0), &zeal))
+ return false;
+
+ JS_SetGCZeal(cx, uint8_t(zeal), JS_DEFAULT_ZEAL_FREQ);
+ return true;
+}
+#endif
+
+const JSFunctionSpec gGlobalFunctions[] =
+{
+ JS_FS("print", Print, 0,0),
+ JS_FS("load", Load, 1,0),
+ JS_FS("quit", Quit, 0,0),
+ JS_FS("version", Version, 1,0),
+ JS_FS("dumpXPC", DumpXPC, 1,0),
+ JS_FS("dump", Dump, 1,0),
+ JS_FS("gc", GC, 0,0),
+ #ifdef JS_GC_ZEAL
+ JS_FS("gczeal", GCZeal, 1,0),
+ #endif
+ JS_FS_END
+};
+
+typedef enum JSShellErrNum
+{
+#define MSG_DEF(name, number, count, exception, format) \
+ name = number,
+#include "jsshell.msg"
+#undef MSG_DEF
+ JSShellErr_Limit
+#undef MSGDEF
+} JSShellErrNum;
+
+} /* anonymous namespace */
+
+void
+XPCShellEnvironment::ProcessFile(JSContext *cx,
+ const char *filename,
+ FILE *file,
+ bool forceTTY)
+{
+ XPCShellEnvironment* env = this;
+
+ JS::Rooted<JS::Value> result(cx);
+ int lineno, startline;
+ bool ok, hitEOF;
+ char *bufp, buffer[4096];
+ JSString *str;
+
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ MOZ_ASSERT(global);
+
+ if (forceTTY) {
+ file = stdin;
+ }
+ else if (!isatty(fileno(file)))
+ {
+ /*
+ * 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);
+
+ JS::CompileOptions options(cx);
+ options.setUTF8(true)
+ .setFileAndLine(filename, 1);
+ JS::Rooted<JSScript*> script(cx);
+ if (JS::Compile(cx, options, file, &script))
+ (void)JS_ExecuteScript(cx, script, &result);
+
+ return;
+ }
+
+ /* It's an interactive filehandle; drop into read-eval-print loop. */
+ lineno = 1;
+ hitEOF = false;
+ do {
+ bufp = buffer;
+ *bufp = '\0';
+
+ /*
+ * 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 = true;
+ break;
+ }
+ bufp += strlen(bufp);
+ lineno++;
+ } while (!JS_BufferIsCompilableUnit(cx, global, buffer, strlen(buffer)));
+
+ /* Clear any pending exception from previous failed compiles. */
+ JS_ClearPendingException(cx);
+ JS::CompileOptions options(cx);
+ options.setFileAndLine("typein", startline);
+ JS::Rooted<JSScript*> script(cx);
+ if (JS_CompileScript(cx, buffer, strlen(buffer), options, &script)) {
+ JS::WarningReporter older;
+
+ ok = JS_ExecuteScript(cx, script, &result);
+ if (ok && !result.isUndefined()) {
+ /* Suppress warnings from JS::ToString(). */
+ older = JS::SetWarningReporter(cx, nullptr);
+ str = JS::ToString(cx, result);
+ JSAutoByteString bytes;
+ if (str)
+ bytes.encodeLatin1(cx, str);
+ JS::SetWarningReporter(cx, older);
+
+ if (!!bytes)
+ fprintf(stdout, "%s\n", bytes.ptr());
+ else
+ ok = false;
+ }
+ }
+ } while (!hitEOF && !env->IsQuitting());
+
+ fprintf(stdout, "\n");
+}
+
+// static
+XPCShellEnvironment*
+XPCShellEnvironment::CreateEnvironment()
+{
+ XPCShellEnvironment* env = new XPCShellEnvironment();
+ if (env && !env->Init()) {
+ delete env;
+ env = nullptr;
+ }
+ return env;
+}
+
+XPCShellEnvironment::XPCShellEnvironment()
+: mQuitting(false)
+{
+}
+
+XPCShellEnvironment::~XPCShellEnvironment()
+{
+ if (GetGlobalObject()) {
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(GetGlobalObject())) {
+ return;
+ }
+ JSContext* cx = jsapi.cx();
+ Rooted<JSObject*> global(cx, GetGlobalObject());
+
+ {
+ JSAutoCompartment ac(cx, global);
+ JS_SetAllNonReservedSlotsToUndefined(cx, global);
+ }
+ mGlobalHolder.reset();
+
+ JS_GC(cx);
+ }
+}
+
+bool
+XPCShellEnvironment::Init()
+{
+ nsresult rv;
+
+ // unbuffer stdout so that output is in the correct order; note that stderr
+ // is unbuffered by default
+ setbuf(stdout, 0);
+
+ AutoSafeJSContext cx;
+
+ mGlobalHolder.init(cx);
+
+ nsCOMPtr<nsIXPConnect> xpc =
+ do_GetService(nsIXPConnect::GetCID());
+ if (!xpc) {
+ NS_ERROR("failed to get nsXPConnect service!");
+ return false;
+ }
+
+ 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 {
+ fprintf(stderr, "+++ Failed to get ScriptSecurityManager service, running without principals");
+ }
+
+ RefPtr<BackstagePass> backstagePass;
+ rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Failed to create backstage pass!");
+ return false;
+ }
+
+ JS::CompartmentOptions options;
+ options.creationOptions().setZone(JS::SystemZone);
+ options.behaviors().setVersion(JSVERSION_LATEST);
+ if (xpc::SharedMemoryEnabled())
+ options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
+
+ nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+ rv = xpc->InitClassesWithNewWrappedGlobal(cx,
+ static_cast<nsIGlobalObject *>(backstagePass),
+ principal, 0,
+ options,
+ getter_AddRefs(holder));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("InitClassesWithNewWrappedGlobal failed!");
+ return false;
+ }
+
+ JS::Rooted<JSObject*> globalObj(cx, holder->GetJSObject());
+ if (!globalObj) {
+ NS_ERROR("Failed to get global JSObject!");
+ return false;
+ }
+ JSAutoCompartment ac(cx, globalObj);
+
+ backstagePass->SetGlobalObject(globalObj);
+
+ JS::Rooted<Value> privateVal(cx, PrivateValue(this));
+ if (!JS_DefineProperty(cx, globalObj, "__XPCShellEnvironment",
+ privateVal,
+ JSPROP_READONLY | JSPROP_PERMANENT,
+ JS_STUBGETTER, JS_STUBSETTER) ||
+ !JS_DefineFunctions(cx, globalObj, gGlobalFunctions) ||
+ !JS_DefineProfilingFunctions(cx, globalObj))
+ {
+ NS_ERROR("JS_DefineFunctions failed!");
+ return false;
+ }
+
+ mGlobalHolder = globalObj;
+
+ FILE* runtimeScriptFile = fopen(kDefaultRuntimeScriptFilename, "r");
+ if (runtimeScriptFile) {
+ fprintf(stdout, "[loading '%s'...]\n", kDefaultRuntimeScriptFilename);
+ ProcessFile(cx, kDefaultRuntimeScriptFilename,
+ runtimeScriptFile, false);
+ fclose(runtimeScriptFile);
+ }
+
+ return true;
+}
+
+bool
+XPCShellEnvironment::EvaluateString(const nsString& aString,
+ nsString* aResult)
+{
+ AutoEntryScript aes(GetGlobalObject(),
+ "ipc XPCShellEnvironment::EvaluateString");
+ JSContext* cx = aes.cx();
+
+ JS::CompileOptions options(cx);
+ options.setFileAndLine("typein", 0);
+ JS::Rooted<JSScript*> script(cx);
+ if (!JS_CompileUCScript(cx, aString.get(), aString.Length(), options,
+ &script))
+ {
+ return false;
+ }
+
+ if (aResult) {
+ aResult->Truncate();
+ }
+
+ JS::Rooted<JS::Value> result(cx);
+ bool ok = JS_ExecuteScript(cx, script, &result);
+ if (ok && !result.isUndefined()) {
+ JS::WarningReporter old = JS::SetWarningReporter(cx, nullptr);
+ JSString* str = JS::ToString(cx, result);
+ nsAutoJSString autoStr;
+ if (str)
+ autoStr.init(cx, str);
+ JS::SetWarningReporter(cx, old);
+
+ if (!autoStr.IsEmpty() && aResult) {
+ aResult->Assign(autoStr);
+ }
+ }
+
+ return true;
+}
diff --git a/ipc/testshell/XPCShellEnvironment.h b/ipc/testshell/XPCShellEnvironment.h
new file mode 100644
index 000000000..c998e6a15
--- /dev/null
+++ b/ipc/testshell/XPCShellEnvironment.h
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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 "nsString.h"
+#include "nsJSPrincipals.h"
+#include "nsContentUtils.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+
+struct JSPrincipals;
+
+namespace mozilla {
+namespace ipc {
+
+class XPCShellEnvironment
+{
+public:
+ static XPCShellEnvironment* CreateEnvironment();
+ ~XPCShellEnvironment();
+
+ void ProcessFile(JSContext *cx, const char *filename, FILE *file, bool forceTTY);
+ bool EvaluateString(const nsString& aString,
+ nsString* aResult = nullptr);
+
+ JSPrincipals* GetPrincipal() {
+ return nsJSPrincipals::get(nsContentUtils::GetSystemPrincipal());
+ }
+
+ JSObject* GetGlobalObject() {
+ return mGlobalHolder;
+ }
+
+ void SetIsQuitting() {
+ mQuitting = true;
+ }
+ bool IsQuitting() {
+ return mQuitting;
+ }
+
+protected:
+ XPCShellEnvironment();
+ bool Init();
+
+private:
+ JS::PersistentRooted<JSObject *> mGlobalHolder;
+
+ bool mQuitting;
+};
+
+} /* namespace ipc */
+} /* namespace mozilla */
+
+#endif /* _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_ */
diff --git a/ipc/testshell/moz.build b/ipc/testshell/moz.build
new file mode 100644
index 000000000..c39273bdd
--- /dev/null
+++ b/ipc/testshell/moz.build
@@ -0,0 +1,37 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla.ipc += [
+ 'TestShellChild.h',
+ 'TestShellParent.h',
+ 'XPCShellEnvironment.h',
+]
+
+XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
+
+SOURCES += [
+ 'TestShellChild.cpp',
+ 'TestShellParent.cpp',
+ 'XPCShellEnvironment.cpp',
+]
+
+IPDL_SOURCES = [
+ 'PTestShell.ipdl',
+ 'PTestShellCommand.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+# For xpcshell error messages and nsAutoJSString
+LOCAL_INCLUDES += [
+ '/dom/base',
+ '/js/xpconnect/src',
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/ipc/testshell/tests/test_ipcshell.js b/ipc/testshell/tests/test_ipcshell.js
new file mode 100644
index 000000000..60af07e34
--- /dev/null
+++ b/ipc/testshell/tests/test_ipcshell.js
@@ -0,0 +1,28 @@
+function callback(result) {
+ do_check_eq(result, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
+ do_test_finished();
+}
+
+function run_test() {
+ do_test_pending();
+
+ do_check_eq(runtime.processType, Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
+
+ sendCommand("load('test_ipcshell_child.js');");
+
+ sendCommand("runtime.processType;", callback);
+
+ [ [ "C", "D" ], [ "D", "C" ], [ "\u010C", "D" ], [ "D", "\u010C" ] ].forEach(
+ function (pair) {
+ do_test_pending();
+ var cmp = pair[0].localeCompare(pair[1]);
+ sendCommand(
+ "'"+ pair[0] +"'.localeCompare('"+ pair[1] +"');",
+ function (result) {
+ do_check_eq(cmp, result);
+ do_test_finished();
+ });
+ })
+}
+load('test_ipcshell_child.js');
+
diff --git a/ipc/testshell/tests/test_ipcshell_child.js b/ipc/testshell/tests/test_ipcshell_child.js
new file mode 100644
index 000000000..d9c9fb6c0
--- /dev/null
+++ b/ipc/testshell/tests/test_ipcshell_child.js
@@ -0,0 +1,9 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+const runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+
+if (typeof(run_test) == "undefined") {
+ run_test = function() {
+ do_check_eq(runtime.processType, Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
+ } }
diff --git a/ipc/testshell/tests/xpcshell.ini b/ipc/testshell/tests/xpcshell.ini
new file mode 100644
index 000000000..26c22fb7b
--- /dev/null
+++ b/ipc/testshell/tests/xpcshell.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+head =
+tail =
+skip-if = toolkit == 'android'
+
+[test_ipcshell.js]
+# Bug 676963: test fails consistently on Android
+fail-if = os == "android"
+[test_ipcshell_child.js]