Bug 648801 (new DOM list bindings) - First stab at implementing an Xray wrapper that can wrap proxy DOM implementations. r=bz/jst/mrbkap.

--HG--
extra : rebase_source : 40e68acd112f07f972211d9818ff05f9a54bd644
This commit is contained in:
Blake Kaplan 2011-05-25 17:30:50 +02:00
Родитель 98ef9e94fc
Коммит 6c4835956c
10 изменённых файлов: 228 добавлений и 44 удалений

Просмотреть файл

@ -40,6 +40,8 @@
#include "dombindings.h"
#include "xpcprivate.h"
#include "xpcquickstubs.h"
#include "XPCWrapper.h"
#include "WrapperFactory.h"
#include "nsIDOMNode.h"
@ -150,8 +152,19 @@ NodeList<T>::setProtoShape(JSObject *obj, uint32 shape)
template<class T>
bool
NodeList<T>::instanceIsNodeListObject(JSContext *cx, JSObject *obj)
NodeList<T>::instanceIsNodeListObject(JSContext *cx, JSObject *obj, JSObject *callee)
{
if (XPCWrapper::IsSecurityWrapper(obj)) {
if (callee && js::GetObjectGlobal(obj) == js::GetObjectGlobal(callee)) {
obj = js::UnwrapObject(obj);
}
else {
obj = XPCWrapper::Unwrap(cx, obj);
if (!obj)
return xpc_qsThrow(cx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
}
}
if (!js::IsProxy(obj) || (js::GetProxyHandler(obj) != &NodeList<T>::instance)) {
// FIXME: Throw a proper DOM exception.
JS_ReportError(cx, "type error: wrong object");
@ -176,7 +189,7 @@ template<class T>
JSBool
NodeList<T>::length_getter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
if (!instanceIsNodeListObject(cx, obj))
if (!instanceIsNodeListObject(cx, obj, NULL))
return false;
PRUint32 length;
getNodeList(obj)->GetLength(&length);
@ -190,7 +203,8 @@ JSBool
NodeList<T>::item(JSContext *cx, uintN argc, jsval *vp)
{
JSObject *obj = JS_THIS_OBJECT(cx, vp);
if (!obj || !instanceIsNodeListObject(cx, obj))
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
if (!obj || !instanceIsNodeListObject(cx, obj, callee))
return false;
if (argc < 1)
return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
@ -208,7 +222,8 @@ JSBool
NodeList<nsIHTMLCollection>::namedItem(JSContext *cx, uintN argc, jsval *vp)
{
JSObject *obj = JS_THIS_OBJECT(cx, vp);
if (!obj || !instanceIsNodeListObject(cx, obj))
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
if (!obj || !instanceIsNodeListObject(cx, obj, callee))
return false;
if (argc < 1)
return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
@ -243,9 +258,6 @@ NodeList<T>::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope)
proto = JS_NewObject(cx, Jsvalify(&sProtoClass), NULL, NULL);
if (!proto)
return NULL;
JSAutoEnterCompartment ac;
if (!ac.enter(cx, proto))
return NULL;
if (!JS_DefinePropertyById(cx, proto, nsDOMClassInfo::sLength_id, JSVAL_VOID,
length_getter, NULL,
@ -328,6 +340,8 @@ NodeList<T>::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool
return false;
if (desc->obj)
return true;
if (WrapperFactory::IsXrayWrapper(proxy))
return resolveNativeName(cx, proxy, id, desc);
return JS_GetPropertyDescriptorById(cx, js::GetObjectProto(proxy), id, JSRESOLVE_QUALIFIED,
desc);
}
@ -439,7 +453,7 @@ NodeList<T>::cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto)
template<class T>
bool
NodeList<T>::checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver, JSObject *proto,
jsid id, Value *vp, bool *hitp)
jsid id, Value *vp, bool *hitp)
{
if (getProtoShape(proxy) != js::GetObjectShape(proto)) {
if (!cacheProtoShape(cx, proxy, proto))
@ -453,6 +467,39 @@ NodeList<T>::checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver
return true;
}
template<class T>
bool
NodeList<T>::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
{
JS_ASSERT(WrapperFactory::IsXrayWrapper(proxy));
if (id == nsDOMClassInfo::sLength_id) {
desc->attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_SHARED;
desc->obj = proxy;
desc->setter = nsnull;
desc->getter = length_getter;
return true;
}
for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoMethods); ++n) {
if (id == sProtoMethods[n].id) {
JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native,
sProtoMethods[n].nargs, 0, proxy, id);
if (!fun)
return false;
JSObject *funobj = JS_GetFunctionObject(fun);
desc->value.setObject(*funobj);
desc->attrs = JSPROP_ENUMERATE;
desc->obj = proxy;
desc->setter = nsnull;
desc->getter = nsnull;
return true;
}
}
return true;
}
template<class T>
bool
NodeList<T>::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)

Просмотреть файл

@ -85,7 +85,7 @@ class NodeList : public NodeListBase {
static Methods sProtoMethods[];
static bool instanceIsNodeListObject(JSContext *cx, JSObject *obj);
static bool instanceIsNodeListObject(JSContext *cx, JSObject *obj, JSObject *callee);
static JSObject *getPrototype(JSContext *cx, XPCWrappedNativeScope *scope);
static T *getNodeList(JSObject *obj);
@ -100,6 +100,9 @@ class NodeList : public NodeListBase {
static bool cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto);
static bool checkForCacheHit(JSContext *cx, JSObject *proxy, JSObject *receiver, JSObject *proto,
jsid id, js::Value *vp, bool *hitp);
static bool resolveNativeName(JSContext *cx, JSObject *proxy, jsid id,
js::PropertyDescriptor *desc);
public:
NodeList();

Просмотреть файл

@ -1609,11 +1609,10 @@ public:
{
JS_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL);
jsval vp;
JS_ALWAYS_TRUE(JS_GetReservedSlot(cx, obj, JSCLASS_GLOBAL_SLOT_COUNT,
&vp));
return JSVAL_IS_VOID(vp) ? nsnull :
static_cast<XPCWrappedNativeScope*>(JSVAL_TO_PRIVATE(vp));
const js::Value &v = js::GetSlot(obj, JSCLASS_GLOBAL_SLOT_COUNT);
return v.isUndefined()
? nsnull
: static_cast<XPCWrappedNativeScope *>(v.toPrivate());
}
void TraceDOMPrototypes(JSTracer *trc);

Просмотреть файл

@ -71,6 +71,7 @@ _CHROME_FILES = \
test_APIExposer.xul \
test_bug664689.xul \
test_precisegc.xul \
test_nodelists.xul \
$(NULL)
# Disabled until this test gets updated to test the new proxy based

Просмотреть файл

@ -0,0 +1,32 @@
<?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"?>
<window title="Test nodelists from chrome"
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">
</body>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
SimpleTest.waitForExplicitFinish();
function go() {
var win = $('ifr').contentWindow;
var list = win.document.getElementsByTagName('p');
is(list.length, 3, "can get the length");
ok(list[0].toString().indexOf("[object HTMLParagraphElement") >= 0, "can get list[0]");
SimpleTest.finish();
}
]]></script>
<iframe id="ifr"
src="http://mochi.test:8888/tests/js/src/xpconnect/tests/mochitest/file_nodelists.html"
onload="go()" />
</window>

Просмотреть файл

@ -93,6 +93,7 @@ _TEST_FILES = bug500931_helper.html \
file_bug650273.html \
file_bug658560.html \
test_bug655297.html \
file_nodelists.html \
$(NULL)
_CHROME_FILES = \

Просмотреть файл

@ -0,0 +1,7 @@
<html>
<body>
<p>
<p>
<p>
</body>
</html>

Просмотреть файл

@ -45,6 +45,7 @@
#include "XPCWrapper.h"
#include "xpcprivate.h"
#include "dombindings.h"
#include "xpcmaps.h"
using namespace js;
@ -248,6 +249,16 @@ GetWrappedNative(JSContext *cx, JSObject *obj)
: nsnull;
}
static bool
CanXray(JSObject *obj, bool *proxy)
{
if (IS_WN_WRAPPER(obj) || js::GetObjectClass(obj)->ext.innerObject) {
*proxy = false;
return true;
}
return (*proxy = dom::instanceIsDOMProxy(obj));
}
JSObject *
WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
uintN flags)
@ -287,12 +298,17 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
wrapper = &CrossOriginWrapper::singleton;
} else {
// Native objects must be wrapped into an X-ray wrapper.
if (IS_WN_WRAPPER(obj) || js::GetObjectClass(obj)->ext.innerObject) {
typedef XrayWrapper<CrossCompartmentWrapper> Xray;
wrapper = &Xray::singleton;
xrayHolder = Xray::createHolder(cx, obj, parent);
if (!xrayHolder)
return nsnull;
bool proxy;
if (CanXray(obj, &proxy)) {
if (proxy) {
wrapper = &XrayProxy::singleton;
} else {
typedef XrayWrapper<CrossCompartmentWrapper> Xray;
wrapper = &Xray::singleton;
xrayHolder = Xray::createHolder(cx, obj, parent);
if (!xrayHolder)
return nsnull;
}
} else {
wrapper = &NoWaiverWrapper::singleton;
}
@ -324,16 +340,20 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
} else if (AccessCheck::isSameOrigin(origin, target)) {
// Same origin we use a transparent wrapper, unless the compartment asks
// for an Xray or the wrapper needs a SOW.
bool proxy;
if (AccessCheck::needsSystemOnlyWrapper(obj)) {
wrapper = &FilteringWrapper<CrossCompartmentWrapper,
OnlyIfSubjectIsSystem>::singleton;
} else if (targetdata && targetdata->wantXrays &&
(IS_WN_WRAPPER(obj) || js::GetObjectClass(obj)->ext.innerObject)) {
typedef XrayWrapper<CrossCompartmentWrapper> Xray;
wrapper = &Xray::singleton;
xrayHolder = Xray::createHolder(cx, obj, parent);
if (!xrayHolder)
return nsnull;
} else if (targetdata && targetdata->wantXrays && CanXray(obj, &proxy)) {
if (proxy) {
wrapper = &XrayProxy::singleton;
} else {
typedef XrayWrapper<CrossCompartmentWrapper> Xray;
wrapper = &Xray::singleton;
xrayHolder = Xray::createHolder(cx, obj, parent);
if (!xrayHolder)
return nsnull;
}
} else {
wrapper = &CrossCompartmentWrapper::singleton;
}
@ -345,25 +365,30 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
// a predefined set of properties. XrayWrapper adds a property
// (.wrappedJSObject) which allows bypassing the XrayWrapper, but
// we filter out access to that property.
if (!IS_WN_WRAPPER(obj) && !js::GetObjectClass(obj)->ext.innerObject) {
bool proxy;
if (!CanXray(obj, &proxy)) {
wrapper = &FilteringWrapper<CrossCompartmentWrapper,
CrossOriginAccessiblePropertiesOnly>::singleton;
} else {
typedef XrayWrapper<CrossCompartmentWrapper> Xray;
// Location objects can become same origin after navigation, so we might
// have to grant transparent access later on.
if (IsLocationObject(obj)) {
wrapper = &FilteringWrapper<Xray,
SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton;
if (proxy) {
wrapper = &XrayProxy::singleton;
} else {
wrapper = &FilteringWrapper<Xray,
CrossOriginAccessiblePropertiesOnly>::singleton;
}
typedef XrayWrapper<CrossCompartmentWrapper> Xray;
xrayHolder = Xray::createHolder(cx, obj, parent);
if (!xrayHolder)
return nsnull;
// Location objects can become same origin after navigation, so we might
// have to grant transparent access later on.
if (IsLocationObject(obj)) {
wrapper = &FilteringWrapper<Xray,
SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton;
} else {
wrapper = &FilteringWrapper<Xray,
CrossOriginAccessiblePropertiesOnly>::singleton;
}
xrayHolder = Xray::createHolder(cx, obj, parent);
if (!xrayHolder)
return nsnull;
}
}
}

Просмотреть файл

@ -1051,6 +1051,58 @@ XrayWrapper<Base>::createHolder(JSContext *cx, JSObject *wrappedNative, JSObject
return holder;
}
XrayProxy::XrayProxy(uintN flags)
: XrayWrapper<CrossCompartmentWrapper>(flags)
{
}
XrayProxy::~XrayProxy()
{
}
bool
XrayProxy::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
bool set, js::PropertyDescriptor *desc)
{
return Proxy::getPropertyDescriptor(cx, wrapper, id, set, desc);
}
bool
XrayProxy::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
bool set, js::PropertyDescriptor *desc)
{
return Proxy::getOwnPropertyDescriptor(cx, wrapper, id, set, desc);
}
bool
XrayProxy::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
js::PropertyDescriptor *desc)
{
return Proxy::defineProperty(cx, wrapper, id, desc);
}
bool
XrayProxy::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props)
{
return Proxy::getOwnPropertyNames(cx, wrapper, props);
}
bool
XrayProxy::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp)
{
return Proxy::delete_(cx, wrapper, id, bp);
}
bool
XrayProxy::enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props)
{
return Proxy::enumerate(cx, wrapper, props);
}
XrayProxy
XrayProxy::singleton(0);
#define XPCNW XrayWrapper<CrossCompartmentWrapper>
#define SCNW XrayWrapper<Wrapper>

Просмотреть файл

@ -64,9 +64,6 @@ class XrayWrapper : public Base {
XrayWrapper(uintN flags);
virtual ~XrayWrapper();
bool resolveWrappedJSObject(JSContext *cx, JSObject *wrapper, jsid id,
bool set, js::PropertyDescriptor *desc);
/* Fundamental proxy traps. */
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
bool set, js::PropertyDescriptor *desc);
@ -103,4 +100,24 @@ class XrayWrapper : public Base {
js::PropertyDescriptor *desc);
};
class XrayProxy : public XrayWrapper<js::CrossCompartmentWrapper> {
public:
XrayProxy(uintN flags);
virtual ~XrayProxy();
virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
bool set, js::PropertyDescriptor *desc);
virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
bool set, js::PropertyDescriptor *desc);
virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
js::PropertyDescriptor *desc);
virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper,
js::AutoIdVector &props);
virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp);
virtual bool enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props);
// XrayWrapper's fix implementation works for us.
static XrayProxy singleton;
};
}