From 220258d4985ed1196d52f0edf6a6770ee1794993 Mon Sep 17 00:00:00 2001 From: "jst%netscape.com" Date: Fri, 21 Jun 2002 20:55:17 +0000 Subject: [PATCH] Fixing bug 150824. Don't crash if initialization of an XBL JS class fails. r=dbradley@netscape.com, sr=alecf@netscape.com --- content/xbl/src/nsXBLBinding.cpp | 112 +++++++++++++--------- content/xbl/src/nsXBLBinding.h | 7 +- content/xbl/src/nsXBLPrototypeBinding.cpp | 86 ++--------------- content/xbl/src/nsXBLService.h | 2 +- 4 files changed, 81 insertions(+), 126 deletions(-) diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index f26e2f0bfec5..57810c95d3c9 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -125,7 +125,7 @@ XBLFinalize(JSContext *cx, JSObject *obj) c->Drop(); } -nsXBLJSClass::nsXBLJSClass(const nsCString& aClassName) +nsXBLJSClass::nsXBLJSClass(const nsAFlatCString& aClassName) { memset(this, 0, sizeof(nsXBLJSClass)); next = prev = NS_STATIC_CAST(JSCList*, this); @@ -1229,42 +1229,18 @@ nsXBLBinding::AttributeAffectsStyle(nsISupportsArrayEnumFunc aFunc, void* aData, // Internal helper methods //////////////////////////////////////////////////////////////// -NS_IMETHODIMP -nsXBLBinding::InitClass(const nsCString& aClassName, nsIScriptContext* aContext, - nsIDocument* aDocument, void** aScriptObject, void** aClassObject) +// static +nsresult +nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj, + const nsAFlatCString& aClassName, + void **aClassObject) { - *aClassObject = nsnull; - *aScriptObject = nsnull; - - nsresult rv; - - // Obtain the bound element's current script object. - nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); - NS_ENSURE_SUCCESS(rv, rv); - - JSContext* jscontext = (JSContext*)aContext->GetNativeContext(); - - nsCOMPtr wrapper; - - JSObject* global = ::JS_GetGlobalObject(jscontext); - - rv = xpc->WrapNative(jscontext, global, mBoundElement, - NS_GET_IID(nsISupports), getter_AddRefs(wrapper)); - NS_ENSURE_SUCCESS(rv, rv); - - JSObject* object = nsnull; - - rv = wrapper->GetJSObject(&object); - NS_ENSURE_SUCCESS(rv, rv); - - *aScriptObject = object; - // First ensure our JS class is initialized. - jsval vp; + jsval val; JSObject* proto; - if ((! ::JS_LookupProperty(jscontext, global, aClassName.get(), &vp)) || - JSVAL_IS_PRIMITIVE(vp)) { + if ((!::JS_LookupProperty(cx, global, aClassName.get(), &val)) || + JSVAL_IS_PRIMITIVE(val)) { // We need to initialize the class. nsXBLJSClass* c; @@ -1285,6 +1261,7 @@ nsXBLBinding::InitClass(const nsCString& aClassName, nsIScriptContext* aContext, if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) { // We need to create a struct for this class. c = new nsXBLJSClass(aClassName); + if (!c) return NS_ERROR_OUT_OF_MEMORY; } else { @@ -1306,41 +1283,88 @@ nsXBLBinding::InitClass(const nsCString& aClassName, nsIScriptContext* aContext, // Add c to our table. (nsXBLService::gClassTable)->Put(&key, (void*)c); } - - // Retrieve the current prototype of the JS object. - JSObject* parent_proto = ::JS_GetPrototype(jscontext, object); + + // Retrieve the current prototype of obj. + JSObject* parent_proto = ::JS_GetPrototype(cx, obj); + + // The prototype holds a strong reference to its class struct. + c->Hold(); // Make a new object prototyped by parent_proto and parented by global. - proto = ::JS_InitClass(jscontext, // context + proto = ::JS_InitClass(cx, // context global, // global object parent_proto, // parent proto c, // JSClass - NULL, // JSNative ctor + nsnull, // JSNative ctor 0, // ctor args nsnull, // proto props nsnull, // proto funcs nsnull, // ctor props (static) nsnull); // ctor funcs (static) if (!proto) { + // This will happen if we're OOM or if the security manager + // denies defining the new class... + (nsXBLService::gClassTable)->Remove(&key); - delete c; + + c->Drop(); + return NS_ERROR_OUT_OF_MEMORY; } - // The prototype holds a strong reference to its class struct. - c->Hold(); *aClassObject = (void*)proto; } else { - proto = JSVAL_TO_OBJECT(vp); + proto = JSVAL_TO_OBJECT(val); } // Set the prototype of our object to be the new class. - ::JS_SetPrototype(jscontext, object, proto); + if (!::JS_SetPrototype(cx, obj, proto)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsXBLBinding::InitClass(const nsCString& aClassName, + nsIScriptContext* aContext, + nsIDocument* aDocument, void** aScriptObject, + void** aClassObject) +{ + *aClassObject = nsnull; + *aScriptObject = nsnull; + + nsresult rv; + + // Obtain the bound element's current script object. + nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + JSContext* cx = (JSContext*)aContext->GetNativeContext(); + + nsCOMPtr wrapper; + + JSObject* global = ::JS_GetGlobalObject(cx); + + rv = xpc->WrapNative(cx, global, mBoundElement, NS_GET_IID(nsISupports), + getter_AddRefs(wrapper)); + NS_ENSURE_SUCCESS(rv, rv); + + JSObject* object = nsnull; + rv = wrapper->GetJSObject(&object); + NS_ENSURE_SUCCESS(rv, rv); + + *aScriptObject = object; + + // First ensure our JS class is initialized. + + rv = DoInitJSClass(cx, global, object, aClassName, aClassObject); + NS_ENSURE_SUCCESS(rv, rv); // Root mBoundElement so that it doesn't loose it's binding nsCOMPtr doc; - mBoundElement->GetDocument(*getter_AddRefs(doc)); if (doc) { diff --git a/content/xbl/src/nsXBLBinding.h b/content/xbl/src/nsXBLBinding.h index 392dadaff3fb..0480059f976b 100644 --- a/content/xbl/src/nsXBLBinding.h +++ b/content/xbl/src/nsXBLBinding.h @@ -50,6 +50,7 @@ class nsSupportsHashtable; class nsIXBLService; class nsFixedSizeAllocator; class nsXBLEventHandler; +struct JSContext; // *********************************************************************/ // The XBLBinding class @@ -131,7 +132,11 @@ public: void InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement); static nsresult GetTextData(nsIContent *aParent, nsString& aResult); - + + static nsresult DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj, + const nsAFlatCString& aClassName, + void **aClassObject); + // Static members static PRUint32 gRefCnt; diff --git a/content/xbl/src/nsXBLPrototypeBinding.cpp b/content/xbl/src/nsXBLPrototypeBinding.cpp index 6a7c0cad63b4..42f4b18ab171 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.cpp +++ b/content/xbl/src/nsXBLPrototypeBinding.cpp @@ -865,93 +865,19 @@ nsXBLPrototypeBinding::GetImmediateChild(nsIAtom* aTag, nsIContent** aResult) } NS_IMETHODIMP -nsXBLPrototypeBinding::InitClass(const nsCString& aClassName, nsIScriptContext * aContext, void * aScriptObject, void ** aClassObject) +nsXBLPrototypeBinding::InitClass(const nsCString& aClassName, + nsIScriptContext * aContext, + void * aScriptObject, void ** aClassObject) { NS_ENSURE_ARG_POINTER (aClassObject); *aClassObject = nsnull; - JSContext* jscontext = (JSContext*)aContext->GetNativeContext(); - JSObject* global = ::JS_GetGlobalObject(jscontext); + JSContext* cx = (JSContext*)aContext->GetNativeContext(); JSObject* scriptObject = (JSObject*) aScriptObject; - // First ensure our JS class is initialized. - jsval vp; - JSObject* proto; - - if ((! ::JS_LookupProperty(jscontext, global, aClassName.get(), &vp)) || JSVAL_IS_PRIMITIVE(vp)) { - // We need to initialize the class. - nsXBLJSClass* c; - void* classObject; - nsCStringKey key(aClassName); - classObject = (nsXBLService::gClassTable)->Get(&key); - - if (classObject) { - c = NS_STATIC_CAST(nsXBLJSClass*, classObject); - // If c is on the LRU list (i.e., not linked to itself), remove it now! - JSCList* link = NS_STATIC_CAST(JSCList*, c); - if (c->next != link) { - JS_REMOVE_AND_INIT_LINK(link); - nsXBLService::gClassLRUListLength--; - } - } - else { - if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) { - // We need to create a struct for this class. - c = new nsXBLJSClass(aClassName); - if (!c) - return NS_ERROR_OUT_OF_MEMORY; - } - else { - // Pull the least recently used class struct off the list. - JSCList* lru = (nsXBLService::gClassLRUList).next; - JS_REMOVE_AND_INIT_LINK(lru); - nsXBLService::gClassLRUListLength--; - - // Remove any mapping from the old name to the class struct. - c = NS_STATIC_CAST(nsXBLJSClass*, lru); - nsCStringKey oldKey(c->name); - (nsXBLService::gClassTable)->Remove(&oldKey); - - // Change the class name and we're done. - nsMemory::Free((void*) c->name); - c->name = ToNewCString(aClassName); - } - - // Add c to our table. - (nsXBLService::gClassTable)->Put(&key, (void*)c); - } - - // Retrieve the current prototype of the JS object. - JSObject* parent_proto = ::JS_GetPrototype(jscontext, scriptObject); - - // Make a new object prototyped by parent_proto and parented by global. - proto = ::JS_InitClass(jscontext, // context - global, // global object - parent_proto, // parent proto - c, // JSClass - NULL, // JSNative ctor - 0, // ctor args - nsnull, // proto props - nsnull, // proto funcs - nsnull, // ctor props (static) - nsnull); // ctor funcs (static) - if (!proto) { - (nsXBLService::gClassTable)->Remove(&key); - delete c; - return NS_ERROR_OUT_OF_MEMORY; - } - - // The prototype holds a strong reference to its class struct. - c->Hold(); - *aClassObject = (void *) proto; - } - else - proto = JSVAL_TO_OBJECT(vp); - - ::JS_SetPrototype(jscontext, scriptObject, proto); - - return NS_OK; + return nsXBLBinding::DoInitJSClass(cx, ::JS_GetGlobalObject(cx), + scriptObject, aClassName, aClassObject); } void diff --git a/content/xbl/src/nsXBLService.h b/content/xbl/src/nsXBLService.h index 28d5dbd1b260..d4e6d7bf26d6 100644 --- a/content/xbl/src/nsXBLService.h +++ b/content/xbl/src/nsXBLService.h @@ -148,7 +148,7 @@ private: nsrefcnt Destroy(); public: - nsXBLJSClass(const nsCString& aClassName); + nsXBLJSClass(const nsAFlatCString& aClassName); ~nsXBLJSClass() { nsMemory::Free((void*) name); } nsrefcnt Hold() { return ++mRefCnt; }