зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
98ef9e94fc
Коммит
6c4835956c
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче