mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 726949. Instead of using the given proto for the sandbox directly, use a proxy that forwards to the given proto but rebinds all getters/setters/methods to use the given proto, not the sandbox global, as this. r=bholley, a=tracking-firefox
The code in XPCQuickStubs.h just moved from XPCQuickStubs.cpp.
This commit is contained in:
parent
5533674517
commit
757816f10d
@ -51,7 +51,7 @@ namespace js {
|
||||
|
||||
class DummyFrameGuard;
|
||||
|
||||
/* Base class that just implements no-op forwarding methods for funamental
|
||||
/* Base class that just implements no-op forwarding methods for fundamental
|
||||
* traps. This is meant to be used as a base class for ProxyHandlers that
|
||||
* want transparent forwarding behavior but don't want to use the derived
|
||||
* traps and other baggage of js::Wrapper.
|
||||
|
@ -3066,6 +3066,98 @@ class Identity : public nsISupports
|
||||
|
||||
NS_IMPL_ISUPPORTS0(Identity)
|
||||
|
||||
class SandboxProxyHandler : public js::AbstractWrapper {
|
||||
public:
|
||||
SandboxProxyHandler() : js::AbstractWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id,
|
||||
bool set, PropertyDescriptor *desc);
|
||||
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
|
||||
jsid id, bool set,
|
||||
PropertyDescriptor *desc);
|
||||
};
|
||||
|
||||
SandboxProxyHandler sandboxProxyHandler;
|
||||
|
||||
template<typename Op>
|
||||
bool BindPropertyOp(JSContext *cx, JSObject *targetObj, Op& op,
|
||||
PropertyDescriptor *desc, jsid id, unsigned attrFlag)
|
||||
{
|
||||
if (!op) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *func;
|
||||
if (desc->attrs & attrFlag) {
|
||||
// Already an object
|
||||
func = JS_FUNC_TO_DATA_PTR(JSObject *, op);
|
||||
} else {
|
||||
// We have an actual property op. For getters, we use 0
|
||||
// args, for setters we use 1 arg.
|
||||
uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1;
|
||||
func = GeneratePropertyOp(cx, desc->obj, id, args, op);
|
||||
if (!func)
|
||||
return false;
|
||||
}
|
||||
func = JS_BindCallable(cx, func, targetObj);
|
||||
if (!func)
|
||||
return false;
|
||||
op = JS_DATA_TO_FUNC_PTR(Op, func);
|
||||
desc->attrs |= attrFlag;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SandboxProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy,
|
||||
jsid id, bool set,
|
||||
PropertyDescriptor *desc)
|
||||
{
|
||||
JSObject *obj = wrappedObject(proxy);
|
||||
JS_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
|
||||
// XXXbz Not sure about the JSRESOLVE_QUALIFIED here, but we have
|
||||
// no way to tell for sure whether to use it.
|
||||
if (!JS_GetPropertyDescriptorById(cx, obj, id,
|
||||
(set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED,
|
||||
desc))
|
||||
return false;
|
||||
|
||||
if (!desc->obj)
|
||||
return true; // No property, nothing to do
|
||||
|
||||
// Now fix up the getter/setter/value as needed to be bound to desc->obj
|
||||
if (!BindPropertyOp(cx, obj, desc->getter, desc, id, JSPROP_GETTER))
|
||||
return false;
|
||||
if (!BindPropertyOp(cx, obj, desc->setter, desc, id, JSPROP_SETTER))
|
||||
return false;
|
||||
if (desc->value.isObject()) {
|
||||
JSObject* val = &desc->value.toObject();
|
||||
if (JS_ObjectIsCallable(cx, val)) {
|
||||
val = JS_BindCallable(cx, val, obj);
|
||||
if (!val)
|
||||
return false;
|
||||
desc->value = ObjectValue(*val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
|
||||
jsid id, bool set,
|
||||
PropertyDescriptor *desc)
|
||||
{
|
||||
if (!getPropertyDescriptor(cx, proxy, id, set, desc))
|
||||
return false;
|
||||
|
||||
if (desc->obj != wrappedObject(proxy))
|
||||
desc->obj = nsnull;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop, JSObject *proto,
|
||||
bool wantXrays, const nsACString &sandboxName, nsISupports *identityPtr)
|
||||
@ -3134,6 +3226,20 @@ xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop, JSOb
|
||||
proto = JSVAL_TO_OBJECT(v);
|
||||
}
|
||||
|
||||
// Now check what sort of thing we've got in |proto|
|
||||
JSObject *unwrappedProto = js::UnwrapObject(proto, false);
|
||||
js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto);
|
||||
if (IS_WRAPPER_CLASS(unwrappedClass) ||
|
||||
mozilla::dom::bindings::IsDOMClass(Jsvalify(unwrappedClass))) {
|
||||
// Wrap it up in a proxy that will do the right thing in terms
|
||||
// of this-binding for methods.
|
||||
proto = js::NewProxyObject(cx, &sandboxProxyHandler,
|
||||
ObjectValue(*proto), nsnull,
|
||||
sandbox);
|
||||
if (!proto)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
ok = JS_SetPrototype(cx, sandbox, proto);
|
||||
if (!ok)
|
||||
return NS_ERROR_XPC_UNEXPECTED;
|
||||
|
@ -116,54 +116,6 @@ LookupInterfaceOrAncestor(PRUint32 tableSize, const xpc_qsHashEntry *table,
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Apply |op| to |obj|, |id|, and |vp|. If |op| is a setter, treat the assignment as lenient.
|
||||
template<typename Op>
|
||||
static inline JSBool ApplyPropertyOp(JSContext *cx, Op op, JSObject *obj, jsid id, jsval *vp);
|
||||
|
||||
template<>
|
||||
inline JSBool
|
||||
ApplyPropertyOp<JSPropertyOp>(JSContext *cx, JSPropertyOp op, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
return op(cx, obj, id, vp);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline JSBool
|
||||
ApplyPropertyOp<JSStrictPropertyOp>(JSContext *cx, JSStrictPropertyOp op, JSObject *obj,
|
||||
jsid id, jsval *vp)
|
||||
{
|
||||
return op(cx, obj, id, true, vp);
|
||||
}
|
||||
|
||||
template<typename Op>
|
||||
static JSBool
|
||||
PropertyOpForwarder(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
// Layout:
|
||||
// this = our this
|
||||
// property op to call = callee reserved slot 0
|
||||
// name of the property = callee reserved slot 1
|
||||
|
||||
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
|
||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
jsval v = js::GetFunctionNativeReserved(callee, 0);
|
||||
|
||||
JSObject *ptrobj = JSVAL_TO_OBJECT(v);
|
||||
Op *popp = static_cast<Op *>(JS_GetPrivate(ptrobj));
|
||||
|
||||
v = js::GetFunctionNativeReserved(callee, 1);
|
||||
|
||||
jsval argval = (argc > 0) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
|
||||
jsid id;
|
||||
if (!JS_ValueToId(cx, argval, &id))
|
||||
return false;
|
||||
JS_SET_RVAL(cx, vp, argval);
|
||||
return ApplyPropertyOp<Op>(cx, *popp, obj, id, vp);
|
||||
}
|
||||
|
||||
static void
|
||||
PointerFinalize(JSFreeOp *fop, JSObject *obj)
|
||||
{
|
||||
@ -171,44 +123,13 @@ PointerFinalize(JSFreeOp *fop, JSObject *obj)
|
||||
delete popp;
|
||||
}
|
||||
|
||||
static JSClass
|
||||
JSClass
|
||||
PointerHolderClass = {
|
||||
"Pointer", JSCLASS_HAS_PRIVATE,
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize
|
||||
};
|
||||
|
||||
template<typename Op>
|
||||
static JSObject *
|
||||
GeneratePropertyOp(JSContext *cx, JSObject *obj, jsid id, unsigned argc, Op pop)
|
||||
{
|
||||
// The JS engine provides two reserved slots on function objects for
|
||||
// XPConnect to use. Use them to stick the necessary info here.
|
||||
JSFunction *fun =
|
||||
js::NewFunctionByIdWithReserved(cx, PropertyOpForwarder<Op>, argc, 0, obj, id);
|
||||
if (!fun)
|
||||
return nsnull;
|
||||
|
||||
JSObject *funobj = JS_GetFunctionObject(fun);
|
||||
|
||||
JS::AutoObjectRooter tvr(cx, funobj);
|
||||
|
||||
// Unfortunately, we cannot guarantee that Op is aligned. Use a
|
||||
// second object to work around this.
|
||||
JSObject *ptrobj = JS_NewObject(cx, &PointerHolderClass, nsnull, funobj);
|
||||
if (!ptrobj)
|
||||
return nsnull;
|
||||
Op *popp = new Op;
|
||||
if (!popp)
|
||||
return nsnull;
|
||||
*popp = pop;
|
||||
JS_SetPrivate(ptrobj, popp);
|
||||
|
||||
js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(ptrobj));
|
||||
js::SetFunctionNativeReserved(funobj, 1, js::IdToJsval(id));
|
||||
return funobj;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
ReifyPropertyOps(JSContext *cx, JSObject *obj, jsid id, unsigned orig_attrs,
|
||||
JSPropertyOp getter, JSStrictPropertyOp setter,
|
||||
|
@ -45,6 +45,8 @@
|
||||
|
||||
#include "nsINode.h"
|
||||
|
||||
#include "jsatom.h"
|
||||
|
||||
/* XPCQuickStubs.h - Support functions used only by quick stubs. */
|
||||
|
||||
class XPCCallContext;
|
||||
@ -680,4 +682,85 @@ xpc_qsSameResult(PRInt32 result1, PRInt32 result2)
|
||||
#define XPC_QS_ASSERT_CONTEXT_OK(cx) ((void) 0)
|
||||
#endif
|
||||
|
||||
// Apply |op| to |obj|, |id|, and |vp|. If |op| is a setter, treat the assignment as lenient.
|
||||
template<typename Op>
|
||||
static inline JSBool ApplyPropertyOp(JSContext *cx, Op op, JSObject *obj, jsid id, jsval *vp);
|
||||
|
||||
template<>
|
||||
inline JSBool
|
||||
ApplyPropertyOp<JSPropertyOp>(JSContext *cx, JSPropertyOp op, JSObject *obj, jsid id, jsval *vp)
|
||||
{
|
||||
return op(cx, obj, id, vp);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline JSBool
|
||||
ApplyPropertyOp<JSStrictPropertyOp>(JSContext *cx, JSStrictPropertyOp op, JSObject *obj,
|
||||
jsid id, jsval *vp)
|
||||
{
|
||||
return op(cx, obj, id, true, vp);
|
||||
}
|
||||
|
||||
template<typename Op>
|
||||
JSBool
|
||||
PropertyOpForwarder(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
// Layout:
|
||||
// this = our this
|
||||
// property op to call = callee reserved slot 0
|
||||
// name of the property = callee reserved slot 1
|
||||
|
||||
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
|
||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
jsval v = js::GetFunctionNativeReserved(callee, 0);
|
||||
|
||||
JSObject *ptrobj = JSVAL_TO_OBJECT(v);
|
||||
Op *popp = static_cast<Op *>(JS_GetPrivate(ptrobj));
|
||||
|
||||
v = js::GetFunctionNativeReserved(callee, 1);
|
||||
|
||||
jsval argval = (argc > 0) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
|
||||
jsid id;
|
||||
if (!JS_ValueToId(cx, argval, &id))
|
||||
return false;
|
||||
JS_SET_RVAL(cx, vp, argval);
|
||||
return ApplyPropertyOp<Op>(cx, *popp, obj, id, vp);
|
||||
}
|
||||
|
||||
extern JSClass PointerHolderClass;
|
||||
|
||||
template<typename Op>
|
||||
JSObject *
|
||||
GeneratePropertyOp(JSContext *cx, JSObject *obj, jsid id, unsigned argc, Op pop)
|
||||
{
|
||||
// The JS engine provides two reserved slots on function objects for
|
||||
// XPConnect to use. Use them to stick the necessary info here.
|
||||
JSFunction *fun =
|
||||
js::NewFunctionByIdWithReserved(cx, PropertyOpForwarder<Op>, argc, 0, obj, id);
|
||||
if (!fun)
|
||||
return nsnull;
|
||||
|
||||
JSObject *funobj = JS_GetFunctionObject(fun);
|
||||
|
||||
JS::AutoObjectRooter tvr(cx, funobj);
|
||||
|
||||
// Unfortunately, we cannot guarantee that Op is aligned. Use a
|
||||
// second object to work around this.
|
||||
JSObject *ptrobj = JS_NewObject(cx, &PointerHolderClass, nsnull, funobj);
|
||||
if (!ptrobj)
|
||||
return nsnull;
|
||||
Op *popp = new Op;
|
||||
if (!popp)
|
||||
return nsnull;
|
||||
*popp = pop;
|
||||
JS_SetPrivate(ptrobj, popp);
|
||||
|
||||
js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(ptrobj));
|
||||
js::SetFunctionNativeReserved(funobj, 1, js::IdToJsval(id));
|
||||
return funobj;
|
||||
}
|
||||
|
||||
#endif /* xpcquickstubs_h___ */
|
||||
|
@ -78,6 +78,7 @@ _CHROME_FILES = \
|
||||
test_weakmaps.xul \
|
||||
test_exnstack.xul \
|
||||
test_weakref.xul \
|
||||
test_bug726949.xul \
|
||||
$(NULL)
|
||||
|
||||
# Disabled until this test gets updated to test the new proxy based
|
||||
|
37
js/xpconnect/tests/chrome/test_bug726949.xul
Normal file
37
js/xpconnect/tests/chrome/test_bug726949.xul
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=726949
|
||||
-->
|
||||
<window title="Mozilla Bug 726949"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- 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=726949"
|
||||
target="_blank">Mozilla Bug 726949</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
/** Test for Bug 726949 **/
|
||||
var Cu = Components.utils;
|
||||
var s = new Cu.Sandbox(window, { sandboxPrototype: window } );
|
||||
var t;
|
||||
var desc;
|
||||
try {
|
||||
t = Cu.evalInSandbox('top', s);
|
||||
is(t, window.top, "Should have gotten the right thing back");
|
||||
desc = Cu.evalInSandbox('Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), "Cu")', s);
|
||||
isnot(desc, undefined,
|
||||
"Should have an own 'Cu' property");
|
||||
is(desc.value, Cu, "Should have the right value");
|
||||
} catch (e) {
|
||||
ok(false, "Should not get an exception: " + e);
|
||||
}
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
Loading…
Reference in New Issue
Block a user