author | Benjamin Smedberg <benjamin@smedbergs.us> |
Thu, 08 Jul 2010 09:40:07 -0700 | |
changeset 47305 | 053bf6cd63e9182f3b2aad9993b81fcc46163559 |
parent 47304 | c72536855cad751d7410d333425bb3d4c0eafd79 |
child 47306 | 9d22ad64d6f062b1f7853f18e287fe2155de8020 |
push id | unknown |
push user | unknown |
push date | unknown |
reviewers | bent |
bugs | 568698 |
milestone | 2.0b2pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/jetpack/JetpackActorCommon.cpp +++ b/js/jetpack/JetpackActorCommon.cpp @@ -66,17 +66,17 @@ public: typedef size_t IdType; typedef js::HashMap< KeyType, IdType, js::DefaultHasher<KeyType>, js::SystemAllocPolicy > MapType; OpaqueSeenType() { - NS_ASSERTION(map.init(1), "Failed to initialize map"); + (void) map.init(1); } bool ok() { return map.initialized(); } // Reserving 0 as an invalid ID means starting the valid IDs at 1. We // could have reserved a dummy first element of rmap, but that would // have wasted space. static const IdType kInvalidId = 0; @@ -447,34 +447,28 @@ JetpackActorCommon::RecvMessage(JSContex for (PRUint32 i = 0; i < data.Length(); ++i) if (!jsval_from_Variant(cx, data.ElementAt(i), argv + i + 1)) return false; JSObject* implGlobal = JS_GetGlobalObject(cx); js::AutoValueRooter rval(cx); - const uint32 savedOptions = - JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT); - for (PRUint32 i = 0; i < snapshot.Length(); ++i) { Variant* vp = results ? results->AppendElement() : NULL; rval.set(JSVAL_VOID); if (!JS_CallFunctionValue(cx, implGlobal, snapshot[i], argc, argv, rval.addr())) { - // If a receiver throws, we drop the exception on the floor. - JS_ClearPendingException(cx); + (void) JS_ReportPendingException(cx); if (vp) *vp = void_t(); } else if (vp && !jsval_to_Variant(cx, rval.value(), vp)) *vp = void_t(); } - JS_SetOptions(cx, savedOptions); - return true; } JetpackActorCommon::RecList::~RecList() { while (mHead) { RecNode* old = mHead; mHead = mHead->down;
--- a/js/jetpack/JetpackChild.cpp +++ b/js/jetpack/JetpackChild.cpp @@ -39,149 +39,132 @@ #include "jscntxt.h" #include "nsXULAppAPI.h" #include "mozilla/jetpack/JetpackChild.h" #include "mozilla/jetpack/Handle.h" #include "jsarray.h" +#include <stdio.h> + namespace mozilla { namespace jetpack { JetpackChild::JetpackChild() { } JetpackChild::~JetpackChild() { } -#define IMPL_PROP_FLAGS (JSPROP_SHARED | \ - JSPROP_ENUMERATE | \ - JSPROP_READONLY | \ - JSPROP_PERMANENT) -const JSPropertySpec -JetpackChild::sImplProperties[] = { - { "jetpack", 0, IMPL_PROP_FLAGS, UserJetpackGetter, NULL }, - { 0, 0, 0, NULL, NULL } -}; - -#undef IMPL_PROP_FLAGS - #define IMPL_METHOD_FLAGS (JSFUN_FAST_NATIVE | \ JSPROP_ENUMERATE | \ JSPROP_READONLY | \ JSPROP_PERMANENT) const JSFunctionSpec JetpackChild::sImplMethods[] = { JS_FN("sendMessage", SendMessage, 3, IMPL_METHOD_FLAGS), JS_FN("callMessage", CallMessage, 2, IMPL_METHOD_FLAGS), JS_FN("registerReceiver", RegisterReceiver, 2, IMPL_METHOD_FLAGS), JS_FN("unregisterReceiver", UnregisterReceiver, 2, IMPL_METHOD_FLAGS), JS_FN("unregisterReceivers", UnregisterReceivers, 1, IMPL_METHOD_FLAGS), JS_FN("wrap", Wrap, 1, IMPL_METHOD_FLAGS), JS_FN("createHandle", CreateHandle, 0, IMPL_METHOD_FLAGS), + JS_FN("createSandbox", CreateSandbox, 0, IMPL_METHOD_FLAGS), + JS_FN("evalInSandbox", EvalInSandbox, 2, IMPL_METHOD_FLAGS), JS_FS_END }; #undef IMPL_METHOD_FLAGS const JSClass JetpackChild::sGlobalClass = { "JetpackChild::sGlobalClass", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; - + +static void +ReportJetpackErrors(JSContext* cx, const char* message, JSErrorReport* report) +{ + const char* filename = "<unknown>"; + if (report && report->filename) + filename = report->filename; + int lineno = -1; + if (report) + lineno = report->lineno; + + fprintf(stderr, "Jetpack JavaScript Error: %s:%i, %s\n", + filename, lineno, message); +} + bool JetpackChild::Init(base::ProcessHandle aParentProcessHandle, MessageLoop* aIOLoop, IPC::Channel* aChannel) { if (!Open(aChannel, aParentProcessHandle, aIOLoop)) return false; if (!(mRuntime = JS_NewRuntime(32L * 1024L * 1024L)) || - !(mImplCx = JS_NewContext(mRuntime, 8192)) || - !(mUserCx = JS_NewContext(mRuntime, 8192))) + !(mCx = JS_NewContext(mRuntime, 8192))) return false; + JS_SetErrorReporter(mCx, ReportJetpackErrors); + { - JSAutoRequest request(mImplCx); - JS_SetContextPrivate(mImplCx, this); + JSAutoRequest request(mCx); + JS_SetContextPrivate(mCx, this); JSObject* implGlobal = - JS_NewGlobalObject(mImplCx, const_cast<JSClass*>(&sGlobalClass)); + JS_NewGlobalObject(mCx, const_cast<JSClass*>(&sGlobalClass)); if (!implGlobal || - !JS_InitStandardClasses(mImplCx, implGlobal) || - !JS_DefineProperties(mImplCx, implGlobal, - const_cast<JSPropertySpec*>(sImplProperties)) || - !JS_DefineFunctions(mImplCx, implGlobal, + !JS_InitStandardClasses(mCx, implGlobal) || + !JS_DefineFunctions(mCx, implGlobal, const_cast<JSFunctionSpec*>(sImplMethods))) return false; } - { - JSAutoRequest request(mUserCx); - JS_SetContextPrivate(mUserCx, this); - JSObject* userGlobal = - JS_NewGlobalObject(mUserCx, const_cast<JSClass*>(&sGlobalClass)); - if (!userGlobal || - !JS_InitStandardClasses(mUserCx, userGlobal)) - return false; - } - return true; } void JetpackChild::CleanUp() { ClearReceivers(); - JS_DestroyContext(mUserCx); - JS_DestroyContext(mImplCx); + JS_DestroyContext(mCx); JS_DestroyRuntime(mRuntime); JS_ShutDown(); } void JetpackChild::ActorDestroy(ActorDestroyReason why) { XRE_ShutdownChildProcess(); } bool JetpackChild::RecvSendMessage(const nsString& messageName, const nsTArray<Variant>& data) { - JSAutoRequest request(mImplCx); - return JetpackActorCommon::RecvMessage(mImplCx, messageName, data, NULL); -} - -static bool -Evaluate(JSContext* cx, const nsCString& code) -{ - JSAutoRequest request(cx); - js::AutoValueRooter ignored(cx); - JS_EvaluateScript(cx, JS_GetGlobalObject(cx), code.get(), - code.Length(), "", 1, ignored.addr()); - return true; + JSAutoRequest request(mCx); + return JetpackActorCommon::RecvMessage(mCx, messageName, data, NULL); } bool -JetpackChild::RecvLoadImplementation(const nsCString& code) +JetpackChild::RecvEvalScript(const nsString& code) { - return Evaluate(mImplCx, code); -} + JSAutoRequest request(mCx); -bool -JetpackChild::RecvLoadUserScript(const nsCString& code) -{ - return Evaluate(mUserCx, code); + js::AutoValueRooter ignored(mCx); + (void) JS_EvaluateUCScript(mCx, JS_GetGlobalObject(mCx), code.get(), + code.Length(), "", 1, ignored.addr()); + return true; } PHandleChild* JetpackChild::AllocPHandle() { return new HandleChild(); } @@ -192,30 +175,20 @@ JetpackChild::DeallocPHandle(PHandleChil return true; } JetpackChild* JetpackChild::GetThis(JSContext* cx) { JetpackChild* self = static_cast<JetpackChild*>(JS_GetContextPrivate(cx)); - JS_ASSERT(cx == self->mImplCx || - cx == self->mUserCx); + JS_ASSERT(cx == self->mCx); return self; } -JSBool -JetpackChild::UserJetpackGetter(JSContext* cx, JSObject* obj, jsval id, - jsval* vp) -{ - JSObject* userGlobal = JS_GetGlobalObject(GetThis(cx)->mUserCx); - JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(userGlobal)); - return JS_TRUE; -} - struct MessageResult { nsString msgName; nsTArray<Variant> data; }; static JSBool MessageCommon(JSContext* cx, uintN argc, jsval* vp, MessageResult* result) @@ -423,10 +396,54 @@ JetpackChild::CreateHandle(JSContext* cx return JS_FALSE; } JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(hobj)); return JS_TRUE; } +JSBool +JetpackChild::CreateSandbox(JSContext* cx, uintN argc, jsval* vp) +{ + if (argc > 0) { + JS_ReportError(cx, "createSandbox takes zero arguments"); + return JS_FALSE; + } + + JSObject* obj = JS_NewGlobalObject(cx, const_cast<JSClass*>(&sGlobalClass)); + if (!obj) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); + return JS_InitStandardClasses(cx, obj); +} + +JSBool +JetpackChild::EvalInSandbox(JSContext* cx, uintN argc, jsval* vp) +{ + if (argc != 2) { + JS_ReportError(cx, "evalInSandbox takes two arguments"); + return JS_FALSE; + } + + jsval* argv = JS_ARGV(cx, vp); + + JSObject* obj; + if (!JSVAL_IS_OBJECT(argv[0]) || + !(obj = JSVAL_TO_OBJECT(argv[0])) || + &sGlobalClass != JS_GetClass(cx, obj) || + obj == JS_GetGlobalObject(cx)) { + JS_ReportError(cx, "The first argument to evalInSandbox must be a global object created using createSandbox."); + return JS_FALSE; + } + + JSString* str = JS_ValueToString(cx, argv[1]); + if (!str) + return JS_FALSE; + + js::AutoValueRooter ignored(cx); + return JS_EvaluateUCScript(cx, obj, JS_GetStringChars(str), JS_GetStringLength(str), "", 1, + ignored.addr()); +} + } // namespace jetpack } // namespace mozilla
--- a/js/jetpack/JetpackChild.h +++ b/js/jetpack/JetpackChild.h @@ -64,40 +64,37 @@ public: void CleanUp(); protected: NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); NS_OVERRIDE virtual bool RecvSendMessage(const nsString& messageName, const nsTArray<Variant>& data); - NS_OVERRIDE virtual bool RecvLoadImplementation(const nsCString& code); - NS_OVERRIDE virtual bool RecvLoadUserScript(const nsCString& code); + NS_OVERRIDE virtual bool RecvEvalScript(const nsString& script); NS_OVERRIDE virtual PHandleChild* AllocPHandle(); NS_OVERRIDE virtual bool DeallocPHandle(PHandleChild* actor); private: JSRuntime* mRuntime; - JSContext *mImplCx, *mUserCx; + JSContext *mCx; static JetpackChild* GetThis(JSContext* cx); - static const JSPropertySpec sImplProperties[]; - static JSBool UserJetpackGetter(JSContext* cx, JSObject* obj, jsval idval, - jsval* vp); - static const JSFunctionSpec sImplMethods[]; static JSBool SendMessage(JSContext* cx, uintN argc, jsval *vp); static JSBool CallMessage(JSContext* cx, uintN argc, jsval *vp); static JSBool RegisterReceiver(JSContext* cx, uintN argc, jsval *vp); static JSBool UnregisterReceiver(JSContext* cx, uintN argc, jsval *vp); static JSBool UnregisterReceivers(JSContext* cx, uintN argc, jsval *vp); static JSBool Wrap(JSContext* cx, uintN argc, jsval *vp); static JSBool CreateHandle(JSContext* cx, uintN argc, jsval *vp); + static JSBool CreateSandbox(JSContext* cx, uintN argc, jsval *vp); + static JSBool EvalInSandbox(JSContext* cx, uintN argc, jsval *vp); static const JSClass sGlobalClass; DISALLOW_EVIL_CONSTRUCTORS(JetpackChild); }; } // namespace jetpack } // namespace mozilla
--- a/js/jetpack/JetpackParent.cpp +++ b/js/jetpack/JetpackParent.cpp @@ -58,48 +58,16 @@ JetpackParent::JetpackParent(JSContext* JetpackParent::~JetpackParent() { if (mSubprocess) Destroy(); } NS_IMPL_ISUPPORTS1(JetpackParent, nsIJetpack) -static nsresult -ReadFromURI(const nsAString& aURI, - nsCString* content) -{ - nsCOMPtr<nsIURI> uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), - NS_ConvertUTF16toUTF8(aURI)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIChannel> channel; - NS_NewChannel(getter_AddRefs(channel), uri); - NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE); - - nsCOMPtr<nsIInputStream> input; - rv = channel->Open(getter_AddRefs(input)); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(input, "Channel opened successfully but stream was null?"); - - char buffer[256]; - PRUint32 avail = 0; - input->Available(&avail); - if (avail) { - PRUint32 read = 0; - while (NS_SUCCEEDED(input->Read(buffer, sizeof(buffer), &read)) && read) { - content->Append(buffer, read); - read = 0; - } - } - - return NS_OK; -} - NS_IMETHODIMP JetpackParent::SendMessage(const nsAString& aMessageName) { nsresult rv; nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); NS_ENSURE_SUCCESS(rv, rv); nsAXPCNativeCallContext* ncc = NULL; @@ -154,41 +122,22 @@ JetpackParent::UnregisterReceiver(const NS_IMETHODIMP JetpackParent::UnregisterReceivers(const nsAString& aMessageName) { JetpackActorCommon::UnregisterReceivers(nsString(aMessageName)); return NS_OK; } NS_IMETHODIMP -JetpackParent::LoadImplementation(const nsAString& aURI) +JetpackParent::EvalScript(const nsAString& aScript) { - nsCString code; - nsresult rv = ReadFromURI(aURI, &code); - NS_ENSURE_SUCCESS(rv, rv); - - if (!code.IsEmpty() && - !SendLoadImplementation(code)) - rv = NS_ERROR_FAILURE; - - return rv; -} + if (!SendEvalScript(nsString(aScript))) + return NS_ERROR_FAILURE; -NS_IMETHODIMP -JetpackParent::LoadUserScript(const nsAString& aURI) -{ - nsCString code; - nsresult rv = ReadFromURI(aURI, &code); - NS_ENSURE_SUCCESS(rv, rv); - - if (!code.IsEmpty() && - !SendLoadUserScript(code)) - rv = NS_ERROR_FAILURE; - - return rv; + return NS_OK; } bool JetpackParent::RecvSendMessage(const nsString& messageName, const nsTArray<Variant>& data) { JSAutoRequest request(mContext); return JetpackActorCommon::RecvMessage(mContext, messageName, data, NULL);
--- a/js/jetpack/PJetpack.ipdl +++ b/js/jetpack/PJetpack.ipdl @@ -73,18 +73,17 @@ sync protocol PJetpack { manages PHandle; both: async SendMessage(nsString messageName, Variant[] data); async PHandle(); child: - async LoadImplementation(nsCString code); - async LoadUserScript(nsCString code); + async EvalScript(nsString code); parent: sync CallMessage(nsString messageName, Variant[] data) returns (Variant[] results); };
--- a/js/jetpack/nsIJetpack.idl +++ b/js/jetpack/nsIJetpack.idl @@ -48,15 +48,14 @@ interface nsIJetpack : nsISupports ... */); void registerReceiver(in AString aMessageName, in jsval aReceiver); void unregisterReceiver(in AString aMessageName, in jsval aReceiver); void unregisterReceivers(in AString aMessageName); - void loadImplementation(in AString aURI); - void loadUserScript(in AString aURI); + void evalScript(in AString aScript); nsIVariant createHandle(); void destroy(); };
--- a/js/jetpack/tests/unit/impl.js +++ b/js/jetpack/tests/unit/impl.js @@ -36,8 +36,48 @@ registerReceiver("testarray", registerReceiver("test primitive types", echo); registerReceiver("drop methods", echo); registerReceiver("exception coping", echo); registerReceiver("duplicate receivers", echo); + +function ok(c, msg) +{ + sendMessage("test result", c, msg); +} + +registerReceiver("test sandbox", function() { + var addon = createSandbox(); + ok(typeof(addon) == "object", "typeof(addon)"); + ok("Date" in addon, "addon.Date exists"); + ok(addon.Date !== Date, "Date objects are different"); + + var fn = "var x; var c = 3; function doit() { x = 12; return 4; }"; + evalInSandbox(addon, fn); + + ok(addon.x === undefined, "x is undefined"); + ok(addon.c == 3, "c is 3"); + ok(addon.doit() == 4, "doit called successfully"); + ok(addon.x == 12, "x is now 12"); + + var fn2 = "let function barbar{}"; + try { + evalInSandbox(addon, fn2); + ok(false, "bad syntax should throw"); + } + catch(e) { + ok(true, "bad syntax should throw"); + } + + var fn3 = "throw new Error('just kidding')"; + try { + evalInSandbox(addon, fn3); + ok(false, "thrown error should be caught"); + } + catch(e) { + ok(true, "thrown error should be caught"); + } + + sendMessage("sandbox done"); +});
--- a/js/jetpack/tests/unit/test_jetpack.js +++ b/js/jetpack/tests/unit/test_jetpack.js @@ -1,22 +1,46 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; + var jps = Components.classes["@mozilla.org/jetpack/service;1"] .getService(Components.interfaces.nsIJetpackService); var jetpack = null; load("handle_tests.js"); function createHandle() { return jetpack.createHandle(); } +const PR_RDONLY = 0x1; + +function read_file(f) +{ + var fis = Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(Ci.nsIFileInputStream); + fis.init(f, PR_RDONLY, 0444, Ci.nsIFileInputStream.CLOSE_ON_EOF); + + var lis = Cc["@mozilla.org/intl/converter-input-stream;1"] + .createInstance(Ci.nsIConverterInputStream); + lis.init(fis, "UTF-8", 1024, 0); + + var data = ""; + + var r = {}; + while (lis.readString(0x0FFFFFFF, r)) + data += r.value; + + return data; +} + function run_test() { jetpack = jps.createJetpack(); run_handle_tests(); - jetpack.loadImplementation("file://" + do_get_file("impl.js").path); + jetpack.evalScript(read_file(do_get_file("impl.js"))); var circ1 = {}, circ2 = {}, circ3 = {}, ok = false; ((circ1.next = circ2).next = circ3).next = circ1; try { jetpack.sendMessage("ignored", circ3, circ1); @@ -157,16 +181,23 @@ function run_test() { jetpack.registerReceiver("duplicate receivers", function() { do_check_eq(calls, "."); jetpack.unregisterReceivers("duplicate receivers"); }); jetpack.registerReceiver("duplicate receivers", function() { do_test_finished() }); + jetpack.registerReceiver("test result", function(name, c, msg) { + dump("TEST-INFO | test_jetpack.js | remote check '" + msg + "' result: " + c + "\n"); + do_check_true(c); + }); + jetpack.registerReceiver("sandbox done", do_test_finished); + + do_test_pending(); do_test_pending(); do_test_pending(); do_test_pending(); do_test_pending(); do_test_pending(); do_test_pending(); do_test_pending(); do_test_pending(); @@ -178,14 +209,17 @@ function run_test() { jetpack.sendMessage("gimmeHandle"); jetpack.sendMessage("echo2", obj1, obj2); jetpack.sendMessage("multireturn begin"); jetpack.sendMessage("testarray", testarray); jetpack.sendMessage("test primitive types", undefined, null, true, false, 1, 2, 999, 1/4, "oyez"); jetpack.sendMessage("drop methods", drop); jetpack.sendMessage("exception coping"); + jetpack.sendMessage("duplicate receivers"); + jetpack.sendMessage("test sandbox"); + do_register_cleanup(function() { jetpack.destroy(); }); }
--- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -170,17 +170,17 @@ class XPCShellTests(object): """ Add arguments to run the test or make it interactive. """ if self.interactive: self.xpcsRunArgs = [ '-e', 'print("To start the test, type |_execute_test();|.");', '-i'] else: - self.xpcsRunArgs = ['-e', '_execute_test();'] + self.xpcsRunArgs = ['-e', '_execute_test(); quit(0);'] def getPipes(self): """ Determine the value of the stdout and stderr for the test. Return value is a list (pStdout, pStderr). """ if self.interactive or self.verbose: pStdout = None