Bug 648801 (new DOM list bindings) - Set up prototypes and constructors. r=bz/jst/mrbkap.

--HG--
extra : rebase_source : acf503d1c93a333ba8ef14d0c25160adabbccd4a
This commit is contained in:
Peter Van der Beken 2011-06-14 10:41:15 +02:00
parent 40d23963c2
commit ea0499b427
9 changed files with 169 additions and 23 deletions

View File

@ -603,6 +603,7 @@ DOMCI_DATA(DOMConstructor, void)
0, \
PR_FALSE, \
PR_FALSE, \
NULL, \
NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \
},
@ -619,6 +620,7 @@ DOMCI_DATA(DOMConstructor, void)
0, \
PR_TRUE, \
PR_FALSE, \
NULL, \
NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \
},
@ -1571,6 +1573,7 @@ jsid nsDOMClassInfo::sParent_id = JSID_VOID;
jsid nsDOMClassInfo::sScrollbars_id = JSID_VOID;
jsid nsDOMClassInfo::sLocation_id = JSID_VOID;
jsid nsDOMClassInfo::sConstructor_id = JSID_VOID;
jsid nsDOMClassInfo::sPrototype_id = JSID_VOID;
jsid nsDOMClassInfo::s_content_id = JSID_VOID;
jsid nsDOMClassInfo::sContent_id = JSID_VOID;
jsid nsDOMClassInfo::sMenubar_id = JSID_VOID;
@ -1834,6 +1837,7 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx)
SET_JSID_TO_STRING(sScrollbars_id, cx, "scrollbars");
SET_JSID_TO_STRING(sLocation_id, cx, "location");
SET_JSID_TO_STRING(sConstructor_id, cx, "constructor");
SET_JSID_TO_STRING(sPrototype_id, cx, "prototype");
SET_JSID_TO_STRING(s_content_id, cx, "_content");
SET_JSID_TO_STRING(sContent_id, cx, "content");
SET_JSID_TO_STRING(sMenubar_id, cx, "menubar");
@ -4181,6 +4185,8 @@ nsDOMClassInfo::Init()
sDisableGlobalScopePollutionSupport =
Preferences::GetBool("browser.dom.global_scope_pollution.disabled");
xpc::dom::Register(sClassInfoData);
sIsInitialized = PR_TRUE;
return NS_OK;
@ -4814,6 +4820,7 @@ nsDOMClassInfo::ShutDown()
sScrollbars_id = JSID_VOID;
sLocation_id = JSID_VOID;
sConstructor_id = JSID_VOID;
sPrototype_id = JSID_VOID;
s_content_id = JSID_VOID;
sContent_id = JSID_VOID;
sMenubar_id = JSID_VOID;
@ -6163,6 +6170,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
}
}
// Lookup new DOM bindings.
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
xpc::dom::DefineInterface define =
sClassInfoData[name_struct->mDOMClassInfoID].mDefineDOMInterface;
if (define && xpc::dom::DefineConstructor(cx, obj, define)) {
*did_resolve = true;
return NS_OK;
}
}
// Create the XPConnect prototype for our classinfo, PostCreateProto will
// set up the prototype chain.
nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;

View File

@ -48,6 +48,7 @@
#include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
#include "nsIScriptGlobalObject.h"
#include "nsContentUtils.h"
#include "xpcpublic.h"
namespace mozilla {
class DOMSVGLengthList;
@ -100,6 +101,8 @@ struct nsDOMClassInfoData
PRUint32 mInterfacesBitmap;
bool mChromeOnly;
bool mDisabled;
// For new style DOM bindings.
xpc::dom::DefineInterface mDefineDOMInterface;
#ifdef NS_DEBUG
PRUint32 mDebugID;
#endif
@ -251,6 +254,7 @@ public:
static jsid sScrollbars_id;
static jsid sLocation_id;
static jsid sConstructor_id;
static jsid sPrototype_id;
static jsid s_content_id;
static jsid sContent_id;
static jsid sMenubar_id;

View File

@ -68,6 +68,7 @@
#include "IDBKeyRange.h"
#include "IndexedDatabaseManager.h"
#include "LazyIdleThread.h"
#include "nsIScriptSecurityManager.h"
using namespace mozilla;

View File

@ -55,6 +55,7 @@
#include "AsyncConnectionHelper.h"
#include "IDBEvents.h"
#include "IDBTransaction.h"
#include "nsContentUtils.h"
USING_INDEXEDDB_NAMESPACE

View File

@ -83,8 +83,11 @@ NodeList<T>::NodeList()
template<class T>
NodeList<T> NodeList<T>::instance;
JSBool
interface_hasInstance(JSContext *cx, JSObject *obj, const js::Value *vp, JSBool *bp);
template<>
Class NodeList<nsINodeList>::sProtoClass = {
Class NodeList<nsINodeList>::sInterfaceClass = {
"NodeList",
0,
JS_PropertyStub, /* addProperty */
@ -93,7 +96,14 @@ Class NodeList<nsINodeList>::sProtoClass = {
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub
JS_ConvertStub,
NULL, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
interface_hasInstance
};
template<>
@ -102,7 +112,7 @@ NodeList<nsINodeList>::Methods NodeList<nsINodeList>::sProtoMethods[] = {
};
template<>
Class NodeList<nsIHTMLCollection>::sProtoClass = {
Class NodeList<nsIHTMLCollection>::sInterfaceClass = {
"HTMLCollection",
0,
JS_PropertyStub, /* addProperty */
@ -111,7 +121,14 @@ Class NodeList<nsIHTMLCollection>::sProtoClass = {
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub
JS_ConvertStub,
NULL, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
interface_hasInstance
};
template<>
@ -124,6 +141,24 @@ NodeList<nsIHTMLCollection>::Methods NodeList<nsIHTMLCollection>::sProtoMethods[
{ nsDOMClassInfo::sNamedItem_id, &namedItem, 1 }
};
void
Register(nsDOMClassInfoData *aData)
{
#define REGISTER_PROTO(_dom_class, T) \
aData[eDOMClassInfo_##_dom_class##_id].mDefineDOMInterface = NodeList<T>::getPrototype
REGISTER_PROTO(NodeList, nsINodeList);
REGISTER_PROTO(HTMLCollection, nsIHTMLCollection);
#undef REGISTER_PROTO
}
bool
DefineConstructor(JSContext *cx, JSObject *obj, DefineInterface aDefine)
{
return !!aDefine(cx, XPCWrappedNativeScope::FindInJSObjectScope(cx, obj));
}
template<class T>
T*
NodeList<T>::getNodeList(JSObject *obj)
@ -264,47 +299,109 @@ NodeList<nsIHTMLCollection>::namedItem(JSContext *cx, uintN argc, jsval *vp)
return namedItem(cx, obj, &argv[0], vp);
}
JSBool
interface_hasInstance(JSContext *cx, JSObject *obj, const js::Value *vp, JSBool *bp)
{
if (vp->isObject()) {
jsval prototype;
if (!JS_GetPropertyById(cx, obj, nsDOMClassInfo::sPrototype_id, &prototype) ||
JSVAL_IS_PRIMITIVE(prototype)) {
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
JSMSG_THROW_TYPE_ERROR);
return JS_FALSE;
}
JSObject *other = &vp->toObject();
if (instanceIsProxy(other)) {
ProxyHandler *handler = static_cast<ProxyHandler*>(js::GetProxyHandler(other));
if (handler->isInstanceOf(JSVAL_TO_OBJECT(prototype))) {
*bp = JS_TRUE;
}
else {
JSObject *protoObj = JSVAL_TO_OBJECT(prototype);
JSObject *proto = other;
while ((proto = JS_GetPrototype(cx, proto))) {
if (proto == protoObj) {
*bp = JS_TRUE;
return JS_TRUE;
}
}
*bp = JS_FALSE;
}
return JS_TRUE;
}
}
*bp = JS_FALSE;
return JS_TRUE;
}
template<class T>
JSObject *
NodeList<T>::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope)
{
nsDataHashtable<nsIDHashKey, JSObject*> &cache =
nsDataHashtable<nsDepCharHashKey, JSObject*> &cache =
scope->GetCachedDOMPrototypes();
JSObject *proto;
JSObject *interfacePrototype;
if (cache.IsInitialized()) {
if (cache.Get(NS_GET_TEMPLATE_IID(T), &proto))
return proto;
if (cache.Get(sInterfaceClass.name, &interfacePrototype))
return interfacePrototype;
}
else if (!cache.Init()) {
return NULL;
}
proto = JS_NewObject(cx, Jsvalify(&sProtoClass), NULL, NULL);
if (!proto)
JSObject *global = scope->GetGlobalJSObject();
// We need to pass the object prototype to JS_NewObject. If we pass NULL then the JS engine
// will look up a prototype on the global by using the class' name and we'll recurse into
// getPrototype.
JSObject* proto;
if (!js_GetClassPrototype(cx, global, JSProto_Object, &proto))
return NULL;
if (!JS_DefinePropertyById(cx, proto, nsDOMClassInfo::sLength_id, JSVAL_VOID,
length_getter, NULL,
interfacePrototype = JS_NewObject(cx, NULL, proto, global);
if (!interfacePrototype)
return NULL;
if (!JS_DefinePropertyById(cx, interfacePrototype, nsDOMClassInfo::sLength_id,
JSVAL_VOID, length_getter, NULL,
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_SHARED))
return NULL;
for (size_t n = 0; n < NS_ARRAY_LENGTH(sProtoMethods); ++n) {
jsid id = sProtoMethods[n].id;
JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native, sProtoMethods[n].nargs,
0, js::GetObjectParent(proto), id);
0, js::GetObjectParent(interfacePrototype), id);
if (!fun)
return NULL;
JSObject *funobj = JS_GetFunctionObject(fun);
if (!JS_DefinePropertyById(cx, proto, id, OBJECT_TO_JSVAL(funobj),
if (!JS_DefinePropertyById(cx, interfacePrototype, id, OBJECT_TO_JSVAL(funobj),
NULL, NULL, JSPROP_ENUMERATE))
return NULL;
}
if (!cache.Put(NS_GET_TEMPLATE_IID(T), proto))
JSObject *interface = JS_NewObject(cx, Jsvalify(&sInterfaceClass), NULL, global);
if (!interface ||
!JS_DefinePropertyById(cx, interface, nsDOMClassInfo::sPrototype_id,
OBJECT_TO_JSVAL(interfacePrototype), nsnull, nsnull,
JSPROP_PERMANENT | JSPROP_READONLY))
return NULL;
return proto;
if (!JS_DefinePropertyById(cx, interfacePrototype, nsDOMClassInfo::sConstructor_id,
OBJECT_TO_JSVAL(interface), nsnull, nsnull, 0))
return NULL;
if (!JS_DefineProperty(cx, global, sInterfaceClass.name, OBJECT_TO_JSVAL(interface), NULL,
NULL, 0))
return NULL;
if (!cache.Put(sInterfaceClass.name, interfacePrototype))
return NULL;
return interfacePrototype;
}
template<class T>
@ -740,7 +837,7 @@ template<class T>
bool
NodeList<T>::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
{
*bp = vp->isObject() && js::GetObjectClass(&vp->toObject()) == &sProtoClass;
*bp = vp->isObject() && js::GetObjectClass(&vp->toObject()) == &sInterfaceClass;
return true;
}

View File

@ -50,9 +50,19 @@ class nsIHTMLCollection;
namespace xpc {
namespace dom {
class NodeListBase : public js::ProxyHandler {
class ProxyHandler : public js::ProxyHandler {
protected:
ProxyHandler() : js::ProxyHandler(ProxyFamily())
{
}
public:
NodeListBase() : js::ProxyHandler(ProxyFamily()) {}
virtual bool isInstanceOf(JSObject *prototype) = 0;
};
class NodeListBase : public ProxyHandler {
public:
NodeListBase() : ProxyHandler() {}
static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope,
nsINodeList *aNodeList);
@ -66,9 +76,11 @@ public:
*/
template<class T>
class NodeList : public NodeListBase {
friend void Register(nsDOMClassInfoData *aData);
static NodeList instance;
static js::Class sProtoClass;
static js::Class sInterfaceClass;
struct Methods {
jsid &id;
@ -136,6 +148,10 @@ class NodeList : public NodeListBase {
static bool objIsNodeList(JSObject *obj) {
return js::IsProxy(obj) && js::GetProxyHandler(obj) == &instance;
}
virtual bool isInstanceOf(JSObject *prototype)
{
return js::GetObjectClass(prototype) == &sInterfaceClass;
}
};
}

View File

@ -1600,7 +1600,7 @@ public:
XPCContext *GetContext() { return mContext; }
void SetContext(XPCContext *xpcc) { mContext = nsnull; }
nsDataHashtable<nsIDHashKey, JSObject*>& GetCachedDOMPrototypes()
nsDataHashtable<nsDepCharHashKey, JSObject*>& GetCachedDOMPrototypes()
{
return mCachedDOMPrototypes;
}
@ -1656,7 +1656,7 @@ private:
// about, since all of our scope objects are verified as not doing that.
nsIScriptObjectPrincipal* mScriptObjectPrincipal;
nsDataHashtable<nsIDHashKey, JSObject*> mCachedDOMPrototypes;
nsDataHashtable<nsDepCharHashKey, JSObject*> mCachedDOMPrototypes;
};
JSObject* xpc_CloneJSFunction(XPCCallContext &ccx, JSObject *funobj,

View File

@ -54,6 +54,7 @@
#include "nsTArray.h"
class nsIPrincipal;
struct nsDOMClassInfoData;
static const uint32 XPC_GC_COLOR_BLACK = 0;
static const uint32 XPC_GC_COLOR_GRAY = 1;
@ -284,6 +285,14 @@ enum {
JSPROXYSLOT_EXPANDO = 1
};
typedef JSObject*
(*DefineInterface)(JSContext *cx, XPCWrappedNativeScope *scope);
void
Register(nsDOMClassInfoData *aData);
extern bool
DefineConstructor(JSContext *cx, JSObject *obj, DefineInterface aDefine);
}
}

View File

@ -921,7 +921,7 @@ XPCWrappedNativeScope::RemoveWrappedNativeProtos()
}
static PLDHashOperator
TraceDOMPrototype(const nsID& aKey, JSObject* aData, void* aClosure)
TraceDOMPrototype(const char* aKey, JSObject* aData, void* aClosure)
{
JSTracer *trc = static_cast<JSTracer*>(aClosure);
JS_CALL_OBJECT_TRACER(trc, aData, "DOM prototype");