зеркало из https://github.com/mozilla/gecko-dev.git
Bug 568698 - Instead of fixing two globals in the jetpack process, allow jetpack to create sandboxes in which to load user code and implementation modules, r=bent
This commit is contained in:
Родитель
0b81e9a8e4
Коммит
b3de5c62a7
|
@ -71,7 +71,7 @@ public:
|
|||
> MapType;
|
||||
|
||||
OpaqueSeenType() {
|
||||
NS_ASSERTION(map.init(1), "Failed to initialize map");
|
||||
(void) map.init(1);
|
||||
}
|
||||
|
||||
bool ok() { return map.initialized(); }
|
||||
|
@ -452,24 +452,18 @@ JetpackActorCommon::RecvMessage(JSContext* cx,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
|
||||
#include "jsarray.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace jetpack {
|
||||
|
||||
|
@ -55,18 +57,6 @@ 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 | \
|
||||
|
@ -80,6 +70,8 @@ JetpackChild::sImplMethods[] = {
|
|||
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
|
||||
};
|
||||
|
||||
|
@ -92,7 +84,21 @@ JetpackChild::sGlobalClass = {
|
|||
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,
|
||||
|
@ -102,31 +108,20 @@ JetpackChild::Init(base::ProcessHandle aParentProcessHandle,
|
|||
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;
|
||||
|
||||
{
|
||||
JSAutoRequest request(mImplCx);
|
||||
JS_SetContextPrivate(mImplCx, this);
|
||||
JSObject* implGlobal =
|
||||
JS_NewGlobalObject(mImplCx, const_cast<JSClass*>(&sGlobalClass));
|
||||
if (!implGlobal ||
|
||||
!JS_InitStandardClasses(mImplCx, implGlobal) ||
|
||||
!JS_DefineProperties(mImplCx, implGlobal,
|
||||
const_cast<JSPropertySpec*>(sImplProperties)) ||
|
||||
!JS_DefineFunctions(mImplCx, implGlobal,
|
||||
const_cast<JSFunctionSpec*>(sImplMethods)))
|
||||
return false;
|
||||
}
|
||||
JS_SetErrorReporter(mCx, ReportJetpackErrors);
|
||||
|
||||
{
|
||||
JSAutoRequest request(mUserCx);
|
||||
JS_SetContextPrivate(mUserCx, this);
|
||||
JSObject* userGlobal =
|
||||
JS_NewGlobalObject(mUserCx, const_cast<JSClass*>(&sGlobalClass));
|
||||
if (!userGlobal ||
|
||||
!JS_InitStandardClasses(mUserCx, userGlobal))
|
||||
JSAutoRequest request(mCx);
|
||||
JS_SetContextPrivate(mCx, this);
|
||||
JSObject* implGlobal =
|
||||
JS_NewGlobalObject(mCx, const_cast<JSClass*>(&sGlobalClass));
|
||||
if (!implGlobal ||
|
||||
!JS_InitStandardClasses(mCx, implGlobal) ||
|
||||
!JS_DefineFunctions(mCx, implGlobal,
|
||||
const_cast<JSFunctionSpec*>(sImplMethods)))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -137,8 +132,7 @@ void
|
|||
JetpackChild::CleanUp()
|
||||
{
|
||||
ClearReceivers();
|
||||
JS_DestroyContext(mUserCx);
|
||||
JS_DestroyContext(mImplCx);
|
||||
JS_DestroyContext(mCx);
|
||||
JS_DestroyRuntime(mRuntime);
|
||||
JS_ShutDown();
|
||||
}
|
||||
|
@ -153,32 +147,21 @@ bool
|
|||
JetpackChild::RecvSendMessage(const nsString& messageName,
|
||||
const nsTArray<Variant>& data)
|
||||
{
|
||||
JSAutoRequest request(mImplCx);
|
||||
return JetpackActorCommon::RecvMessage(mImplCx, messageName, data, NULL);
|
||||
JSAutoRequest request(mCx);
|
||||
return JetpackActorCommon::RecvMessage(mCx, messageName, data, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
Evaluate(JSContext* cx, const nsCString& code)
|
||||
bool
|
||||
JetpackChild::RecvEvalScript(const nsString& code)
|
||||
{
|
||||
JSAutoRequest request(cx);
|
||||
js::AutoValueRooter ignored(cx);
|
||||
JS_EvaluateScript(cx, JS_GetGlobalObject(cx), code.get(),
|
||||
code.Length(), "", 1, ignored.addr());
|
||||
JSAutoRequest request(mCx);
|
||||
|
||||
js::AutoValueRooter ignored(mCx);
|
||||
(void) JS_EvaluateUCScript(mCx, JS_GetGlobalObject(mCx), code.get(),
|
||||
code.Length(), "", 1, ignored.addr());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
JetpackChild::RecvLoadImplementation(const nsCString& code)
|
||||
{
|
||||
return Evaluate(mImplCx, code);
|
||||
}
|
||||
|
||||
bool
|
||||
JetpackChild::RecvLoadUserScript(const nsCString& code)
|
||||
{
|
||||
return Evaluate(mUserCx, code);
|
||||
}
|
||||
|
||||
PHandleChild*
|
||||
JetpackChild::AllocPHandle()
|
||||
{
|
||||
|
@ -197,20 +180,10 @@ 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;
|
||||
|
@ -428,5 +401,49 @@ JetpackChild::CreateHandle(JSContext* cx, uintN argc, jsval* vp)
|
|||
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
|
||||
|
|
|
@ -69,22 +69,17 @@ protected:
|
|||
|
||||
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);
|
||||
|
@ -93,6 +88,8 @@ private:
|
|||
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;
|
||||
|
||||
|
|
|
@ -63,38 +63,6 @@ JetpackParent::~JetpackParent()
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -159,31 +127,12 @@ JetpackParent::UnregisterReceivers(const nsAString& aMessageName)
|
|||
}
|
||||
|
||||
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 (!SendEvalScript(nsString(aScript)))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (!code.IsEmpty() &&
|
||||
!SendLoadImplementation(code))
|
||||
rv = NS_ERROR_FAILURE;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -78,8 +78,7 @@ both:
|
|||
async PHandle();
|
||||
|
||||
child:
|
||||
async LoadImplementation(nsCString code);
|
||||
async LoadUserScript(nsCString code);
|
||||
async EvalScript(nsString code);
|
||||
|
||||
parent:
|
||||
sync CallMessage(nsString messageName,
|
||||
|
|
|
@ -53,8 +53,7 @@ interface nsIJetpack : nsISupports
|
|||
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();
|
||||
|
||||
|
|
|
@ -41,3 +41,43 @@ 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");
|
||||
});
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
var jps = Components.classes["@mozilla.org/jetpack/service;1"]
|
||||
.getService(Components.interfaces.nsIJetpackService);
|
||||
var jetpack = null;
|
||||
|
@ -7,11 +10,32 @@ 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 = {},
|
||||
|
@ -162,6 +186,13 @@ function run_test() {
|
|||
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();
|
||||
|
@ -183,8 +214,11 @@ function run_test() {
|
|||
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();
|
||||
});
|
||||
|
|
|
@ -175,7 +175,7 @@ class XPCShellTests(object):
|
|||
'-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):
|
||||
"""
|
||||
|
|
Загрузка…
Ссылка в новой задаче