diff options
Diffstat (limited to 'ipc/testshell')
-rw-r--r-- | ipc/testshell/PTestShell.ipdl | 27 | ||||
-rw-r--r-- | ipc/testshell/PTestShellCommand.ipdl | 20 | ||||
-rw-r--r-- | ipc/testshell/TestShellChild.cpp | 56 | ||||
-rw-r--r-- | ipc/testshell/TestShellChild.h | 45 | ||||
-rw-r--r-- | ipc/testshell/TestShellParent.cpp | 109 | ||||
-rw-r--r-- | ipc/testshell/TestShellParent.h | 68 | ||||
-rw-r--r-- | ipc/testshell/XPCShellEnvironment.cpp | 541 | ||||
-rw-r--r-- | ipc/testshell/XPCShellEnvironment.h | 64 | ||||
-rw-r--r-- | ipc/testshell/moz.build | 37 | ||||
-rw-r--r-- | ipc/testshell/tests/test_ipcshell.js | 28 | ||||
-rw-r--r-- | ipc/testshell/tests/test_ipcshell_child.js | 9 | ||||
-rw-r--r-- | ipc/testshell/tests/xpcshell.ini | 9 |
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] |