From ea0499b427a01d3ccdf006a99cebf0b3afa95143 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 14 Jun 2011 10:41:15 +0200 Subject: [PATCH] Bug 648801 (new DOM list bindings) - Set up prototypes and constructors. r=bz/jst/mrbkap. --HG-- extra : rebase_source : acf503d1c93a333ba8ef14d0c25160adabbccd4a --- dom/base/nsDOMClassInfo.cpp | 18 +++ dom/base/nsDOMClassInfo.h | 4 + dom/indexedDB/IDBFactory.cpp | 1 + dom/indexedDB/IDBRequest.cpp | 1 + js/src/xpconnect/src/dombindings.cpp | 131 +++++++++++++++--- js/src/xpconnect/src/dombindings.h | 22 ++- js/src/xpconnect/src/xpcprivate.h | 4 +- js/src/xpconnect/src/xpcpublic.h | 9 ++ .../xpconnect/src/xpcwrappednativescope.cpp | 2 +- 9 files changed, 169 insertions(+), 23 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index fcd707b821b3..2dd5317fc72c 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -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 proto_holder; diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index f1e4c043bd4a..369eb309ed29 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -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; diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index 1418e7e9a0a2..8ec391c36e6a 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -68,6 +68,7 @@ #include "IDBKeyRange.h" #include "IndexedDatabaseManager.h" #include "LazyIdleThread.h" +#include "nsIScriptSecurityManager.h" using namespace mozilla; diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index e1fd568d5047..e1ed521361e7 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -55,6 +55,7 @@ #include "AsyncConnectionHelper.h" #include "IDBEvents.h" #include "IDBTransaction.h" +#include "nsContentUtils.h" USING_INDEXEDDB_NAMESPACE diff --git a/js/src/xpconnect/src/dombindings.cpp b/js/src/xpconnect/src/dombindings.cpp index 501973463cb3..20332792c9b6 100644 --- a/js/src/xpconnect/src/dombindings.cpp +++ b/js/src/xpconnect/src/dombindings.cpp @@ -83,8 +83,11 @@ NodeList::NodeList() template NodeList NodeList::instance; +JSBool +interface_hasInstance(JSContext *cx, JSObject *obj, const js::Value *vp, JSBool *bp); + template<> -Class NodeList::sProtoClass = { +Class NodeList::sInterfaceClass = { "NodeList", 0, JS_PropertyStub, /* addProperty */ @@ -93,7 +96,14 @@ Class NodeList::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::Methods NodeList::sProtoMethods[] = { }; template<> -Class NodeList::sProtoClass = { +Class NodeList::sInterfaceClass = { "HTMLCollection", 0, JS_PropertyStub, /* addProperty */ @@ -111,7 +121,14 @@ Class NodeList::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::Methods NodeList::sProtoMethods[ { nsDOMClassInfo::sNamedItem_id, &namedItem, 1 } }; +void +Register(nsDOMClassInfoData *aData) +{ +#define REGISTER_PROTO(_dom_class, T) \ + aData[eDOMClassInfo_##_dom_class##_id].mDefineDOMInterface = NodeList::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 T* NodeList::getNodeList(JSObject *obj) @@ -264,47 +299,109 @@ NodeList::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(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 JSObject * NodeList::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) { - nsDataHashtable &cache = + nsDataHashtable &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 @@ -740,7 +837,7 @@ template bool NodeList::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; } diff --git a/js/src/xpconnect/src/dombindings.h b/js/src/xpconnect/src/dombindings.h index 6b0029d9b882..2a743f89307a 100644 --- a/js/src/xpconnect/src/dombindings.h +++ b/js/src/xpconnect/src/dombindings.h @@ -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 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; + } }; } diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index e9adfc2d8b26..51b3fbdca775 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -1600,7 +1600,7 @@ public: XPCContext *GetContext() { return mContext; } void SetContext(XPCContext *xpcc) { mContext = nsnull; } - nsDataHashtable& GetCachedDOMPrototypes() + nsDataHashtable& GetCachedDOMPrototypes() { return mCachedDOMPrototypes; } @@ -1656,7 +1656,7 @@ private: // about, since all of our scope objects are verified as not doing that. nsIScriptObjectPrincipal* mScriptObjectPrincipal; - nsDataHashtable mCachedDOMPrototypes; + nsDataHashtable mCachedDOMPrototypes; }; JSObject* xpc_CloneJSFunction(XPCCallContext &ccx, JSObject *funobj, diff --git a/js/src/xpconnect/src/xpcpublic.h b/js/src/xpconnect/src/xpcpublic.h index 4a88b2b407da..42b31e0167c2 100644 --- a/js/src/xpconnect/src/xpcpublic.h +++ b/js/src/xpconnect/src/xpcpublic.h @@ -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); + } } diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index a4c1b2da323c..51323a2d7cbc 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -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(aClosure); JS_CALL_OBJECT_TRACER(trc, aData, "DOM prototype");