Bug 634156 - Come up with a way of creating an API in content that exposes real content objects without using a sandbox. r/sr=jst/mossop/gal

--HG--
extra : rebase_source : c3ca469ff33d099b51e140d0a274113884507a03
This commit is contained in:
Blake Kaplan 2011-05-03 13:43:08 -07:00
parent f823587beb
commit 9f1a75137b
6 changed files with 211 additions and 30 deletions

View File

@ -95,24 +95,27 @@ ConsoleAPI.prototype = {
// We need to return an actual content object here, instead of a wrapped
// chrome object. This allows things like console.log.bind() to work.
let sandbox = Cu.Sandbox(aWindow);
let contentObject = Cu.evalInSandbox(
"(function(x) {\
var bind = Function.bind;\
var obj = {\
log: bind.call(x.log, x),\
info: bind.call(x.info, x),\
warn: bind.call(x.warn, x),\
error: bind.call(x.error, x),\
debug: bind.call(x.debug, x),\
trace: bind.call(x.trace, x),\
__noSuchMethod__: function() {}\
};\
Object.defineProperty(obj, '__mozillaConsole__', { value: true });\
return obj;\
})", sandbox)(chromeObject);
let contentObj = Cu.createObjectIn(aWindow);
function genPropDesc(fun) {
return { enumerable: true, configurable: true, writable: true,
value: chromeObject[fun].bind(chromeObject) };
}
const properties = {
log: genPropDesc('log'),
info: genPropDesc('info'),
warn: genPropDesc('warn'),
error: genPropDesc('error'),
debug: genPropDesc('debug'),
trace: genPropDesc('trace'),
__noSuchMethod__: { enumerable: true, configurable: true, writable: true,
value: function() {} },
__mozillaConsole__: { value: true }
};
return contentObject;
Object.defineProperties(contentObj, properties);
Cu.makeObjectPropsNormal(contentObj);
return contentObj;
},
/**

View File

@ -44,6 +44,10 @@
#include "nsIComponentManager.idl"
#include "nsIScriptableInterfaces.idl"
%{C++
#include "jspubtd.h"
%}
interface xpcIJSWeakReference;
/**
@ -123,7 +127,7 @@ interface nsIXPCComponents_utils_Sandbox : nsISupports
/**
* interface of Components.utils
*/
[scriptable, uuid(5f0acf45-135a-48d1-976c-082ce3b24ead)]
[scriptable, uuid(45857850-a08e-4fe9-a582-77fe03e3676f)]
interface nsIXPCComponents_Utils : nsISupports
{
@ -237,6 +241,23 @@ interface nsIXPCComponents_Utils : nsISupports
* @return the corresponding global.
*/
void /* JSObject */ getGlobalForObject(/* in JSObject obj */);
/*
* To be called from JS only.
*
* Returns an object created in |vobj|'s compartment.
*/
[implicit_jscontext]
jsval createObjectIn(in jsval vobj);
/*
* To be called from JS only.
*
* Ensures that all functions come from vobj's scope (and aren't cross
* compartment wrappers).
*/
[implicit_jscontext]
void makeObjectPropsNormal(in jsval vobj);
};
/**

View File

@ -3831,6 +3831,106 @@ nsXPCComponents_Utils::GetGlobalForObject()
return NS_OK;
}
/* jsval createObjectIn(in jsval vobj); */
NS_IMETHODIMP
nsXPCComponents_Utils::CreateObjectIn(const jsval &vobj, JSContext *cx, jsval *rval)
{
if (!cx)
return NS_ERROR_FAILURE;
// first argument must be an object
if(JSVAL_IS_PRIMITIVE(vobj))
return NS_ERROR_XPC_BAD_CONVERT_JS;
JSObject *scope = JSVAL_TO_OBJECT(vobj)->unwrap();
JSObject *obj;
{
JSAutoEnterCompartment ac;
if(!ac.enter(cx, scope))
return NS_ERROR_FAILURE;
obj = JS_NewObject(cx, nsnull, nsnull, scope);
if (!obj)
return NS_ERROR_FAILURE;
}
if (!JS_WrapObject(cx, &obj))
return NS_ERROR_FAILURE;
*rval = OBJECT_TO_JSVAL(obj);
return NS_OK;
}
JSBool
FunctionWrapper(JSContext *cx, uintN argc, jsval *vp)
{
jsval v;
if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &v))
return JS_FALSE;
NS_ASSERTION(JSVAL_IS_OBJECT(v), "weird function");
return JS_CallFunctionValue(cx, JS_THIS_OBJECT(cx, vp), v,
argc, JS_ARGV(cx, vp), vp);
}
JSBool
WrapCallable(JSContext *cx, JSObject *obj, jsid id, JSObject *propobj, jsval *vp)
{
JSFunction *fun = JS_NewFunctionById(cx, FunctionWrapper, 0, 0,
JS_GetGlobalForObject(cx, obj), id);
if (!fun)
return JS_FALSE;
JSObject *funobj = JS_GetFunctionObject(fun);
if (!JS_SetReservedSlot(cx, funobj, 0, OBJECT_TO_JSVAL(propobj)))
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(funobj);
return JS_TRUE;
}
/* void makeObjectPropsNormal(jsval vobj); */
NS_IMETHODIMP
nsXPCComponents_Utils::MakeObjectPropsNormal(const jsval &vobj, JSContext *cx)
{
if (!cx)
return NS_ERROR_FAILURE;
// first argument must be an object
if(JSVAL_IS_PRIMITIVE(vobj))
return NS_ERROR_XPC_BAD_CONVERT_JS;
JSObject *obj = JSVAL_TO_OBJECT(vobj)->unwrap();
JSAutoEnterCompartment ac;
if (!ac.enter(cx, obj))
return NS_ERROR_FAILURE;
js::AutoIdArray ida(cx, JS_Enumerate(cx, obj));
if (!ida)
return NS_ERROR_FAILURE;
for (size_t i = 0; i < ida.length(); ++i) {
jsid id = ida[i];
jsval v;
if (!JS_GetPropertyById(cx, obj, id, &v))
return NS_ERROR_FAILURE;
if (JSVAL_IS_PRIMITIVE(v))
continue;
JSObject *propobj = JSVAL_TO_OBJECT(v);
// TODO Deal with non-functions.
if (!propobj->isWrapper() || !propobj->isCallable())
continue;
if (!WrapCallable(cx, obj, id, propobj, &v) ||
!JS_SetPropertyById(cx, obj, id, &v))
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* string canCreateWrapper (in nsIIDPtr iid); */
NS_IMETHODIMP
nsXPCComponents_Utils::CanCreateWrapper(const nsIID * iid, char **_retval)

View File

@ -66,6 +66,7 @@ _CHROME_FILES = \
test_bug596580.xul \
test_bug654370.xul \
test_bug658560.xul \
test_APIExposer.xul \
$(NULL)
# Disabled until this test gets updated to test the new proxy based

View File

@ -0,0 +1,52 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=634156
-->
<window title="Testing API exposing capabilities"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=634156"
target="_blank">Mozilla Bug 634156</a>
</body>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
const Cu = Components.utils;
var sandbox = new Cu.Sandbox("about:blank");
sandbox.ok = ok;
sandbox.is = is;
Cu.evalInSandbox("Object.defineProperty(Object.prototype, 'getProp', { get: function() { throw 'FAIL: called getter' }, set: function() { throw 'FAIL: called setter'; } })", sandbox);
var obj = Cu.createObjectIn(sandbox);
is(Object.getPrototypeOf(obj), Cu.evalInSandbox("Object.prototype", sandbox),
"Object is a sandbox object");
function genPropDesc(value) {
return { enumerable: true, configurable: true, writable: true,
value: value };
}
const props = {
'getProp': genPropDesc(function() { ok(true, "called prop that shadowed a getter"); }),
'argument': genPropDesc(function(arg) { is(arg, 42, "can pass arguments through"); }),
'returnval': genPropDesc(function() { return 42; })
};
Object.defineProperties(obj, props);
Cu.makeObjectPropsNormal(obj);
sandbox.api = obj;
Cu.evalInSandbox("ok(Object.getPrototypeOf(api) === Object.prototype, 'we have the object we expected'); \
api.getProp(); api.argument(42); is(api.returnval(), 42, 'return value was correct');\
ok(typeof api.getProp === 'function', 'functions are functions');\
ok(Object.getPrototypeOf(api.getProp) === Function.prototype, 'functions come from our scope');", sandbox);
]]></script>
</window>

View File

@ -188,18 +188,22 @@ function createInstallTrigger(window) {
}
};
let sandbox = Cu.Sandbox(window);
let obj = Cu.evalInSandbox(
"(function (x) {\
var bind = Function.bind;\
return {\
enabled: bind.call(x.enabled, x),\
updateEnabled: bind.call(x.updateEnabled, x),\
install: bind.call(x.install, x),\
installChrome: bind.call(x.installChrome, x),\
startSoftwareUpdate: bind.call(x.startSoftwareUpdate, x)\
};\
})", sandbox)(chromeObject);
let obj = Cu.createObjectIn(window);
function genPropDesc(fun) {
return { enumerable: true, configurable: true, writable: true,
value: chromeObject[fun].bind(chromeObject) };
}
const properties = {
'enabled': genPropDesc('enabled'),
'updateEnabled': genPropDesc('updateEnabled'),
'install': genPropDesc('install'),
'installChrome': genPropDesc('installChrome'),
'startSoftwareUpdate': genPropDesc('startSoftwareUpdate')
};
Object.defineProperties(obj, properties);
Cu.makeObjectPropsNormal(obj);
obj.SKIN = chromeObject.SKIN;
obj.LOCALE = chromeObject.LOCALE;