Reference-count XBL JSClass structs, and LRU-cache unreferenced ones up to some quota, to avoid bloat and shutdown crashes due to unavoidable manual delete/last-use misorder (42530, r=hyatt).

This commit is contained in:
brendan%mozilla.org 2000-07-01 02:36:18 +00:00
Родитель d70a4ed2b5
Коммит b864716baa
8 изменённых файлов: 304 добавлений и 160 удалений

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

@ -19,7 +19,7 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -118,16 +118,43 @@ NS_IMPL_ISUPPORTS1(nsXBLAttributeEntry, nsIXBLAttributeEntry)
PR_STATIC_CALLBACK(void) PR_STATIC_CALLBACK(void)
XBLFinalize(JSContext *cx, JSObject *obj) XBLFinalize(JSContext *cx, JSObject *obj)
{ {
nsJSUtils::nsGenericFinalize(cx, obj); nsXBLJSClass* c = NS_STATIC_CAST(nsXBLJSClass*, ::JS_GetClass(cx, obj));
c->Drop();
} }
// nsXBLJSClass::nsXBLJSClass(const nsCString& aClassName)
// XULElement constructor
//
PR_STATIC_CALLBACK(JSBool)
XBLBindingCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{ {
return JS_FALSE; memset(this, 0, sizeof(nsXBLJSClass));
next = prev = NS_STATIC_CAST(JSCList*, this);
name = nsXPIDLCString::Copy(aClassName);
addProperty = delProperty = setProperty = getProperty = ::JS_PropertyStub;
enumerate = ::JS_EnumerateStub;
resolve = ::JS_ResolveStub;
convert = ::JS_ConvertStub;
finalize = XBLFinalize;
}
nsrefcnt
nsXBLJSClass::Destroy()
{
NS_ASSERTION(next == prev && prev == NS_STATIC_CAST(JSCList*, this),
"referenced nsXBLJSClass is on LRU list already!?");
if (nsXBLService::gClassLRUListLength >= nsXBLService::gClassLRUListQuota) {
// Over LRU list quota, just unhash and delete this class.
if (nsXBLService::gClassTable) {
nsStringKey key(name);
(nsXBLService::gClassTable)->Remove(&key);
}
delete this;
} else {
// Put this most-recently-used class on end of the LRU-sorted freelist.
JSCList* mru = NS_STATIC_CAST(JSCList*, this);
JS_APPEND_LINK(mru, &nsXBLService::gClassLRUList);
nsXBLService::gClassLRUListLength++;
}
return 0;
} }
// Static initialization // Static initialization
@ -216,10 +243,10 @@ NS_IMPL_ISUPPORTS1(nsXBLBinding, nsIXBLBinding)
// Constructors/Destructors // Constructors/Destructors
nsXBLBinding::nsXBLBinding(void) nsXBLBinding::nsXBLBinding(void)
: mAttributeTable(nsnull), : mFirstHandler(nsnull),
mInsertionPointTable(nsnull),
mIsStyleBinding(PR_TRUE), mIsStyleBinding(PR_TRUE),
mFirstHandler(nsnull) mAttributeTable(nsnull),
mInsertionPointTable(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
gRefCnt++; gRefCnt++;
@ -955,7 +982,7 @@ nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, PRInt32 aNameSpaceID, PRBool
if (!aRemoveFlag) { if (!aRemoveFlag) {
// Construct a new text node and insert it. // Construct a new text node and insert it.
nsAutoString value; nsAutoString value;
nsresult result = mBoundElement->GetAttribute(aNameSpaceID, aAttribute, value); mBoundElement->GetAttribute(aNameSpaceID, aAttribute, value);
if (!value.IsEmpty()) { if (!value.IsEmpty()) {
nsCOMPtr<nsIDOMText> textNode; nsCOMPtr<nsIDOMText> textNode;
nsCOMPtr<nsIDocument> doc; nsCOMPtr<nsIDocument> doc;
@ -1018,9 +1045,9 @@ nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocumen
// XXX Sanity check to make sure our class name matches // XXX Sanity check to make sure our class name matches
// Pull ourselves out of the proto chain. // Pull ourselves out of the proto chain.
JSContext* jscontext = (JSContext*)context->GetNativeContext(); JSContext* jscontext = (JSContext*)context->GetNativeContext();
JSObject* ourProto = JS_GetPrototype(jscontext, scriptObject); JSObject* ourProto = ::JS_GetPrototype(jscontext, scriptObject);
JSObject* grandProto = JS_GetPrototype(jscontext, ourProto); JSObject* grandProto = ::JS_GetPrototype(jscontext, ourProto);
JS_SetPrototype(jscontext, scriptObject, grandProto); ::JS_SetPrototype(jscontext, scriptObject, grandProto);
} }
} }
} }
@ -1065,69 +1092,84 @@ nsXBLBinding::InitClass(const nsCString& aClassName, nsIScriptContext* aContext,
// First ensure our JS class is initialized. // First ensure our JS class is initialized.
JSContext* jscontext = (JSContext*)aContext->GetNativeContext(); JSContext* jscontext = (JSContext*)aContext->GetNativeContext();
JSObject* proto = nsnull; JSObject* global = ::JS_GetGlobalObject(jscontext);
JSObject* constructor = nsnull;
JSObject* parent_proto = nsnull;
JSObject* global = JS_GetGlobalObject(jscontext);
jsval vp; jsval vp;
JSObject* proto;
if ((PR_TRUE != JS_LookupProperty(jscontext, global, aClassName, &vp)) || if ((! ::JS_LookupProperty(jscontext, global, aClassName, &vp)) ||
!JSVAL_IS_OBJECT(vp) || JSVAL_IS_PRIMITIVE(vp)) {
((constructor = JSVAL_TO_OBJECT(vp)) == nsnull) ||
(PR_TRUE != JS_LookupProperty(jscontext, JSVAL_TO_OBJECT(vp), "prototype", &vp)) ||
!JSVAL_IS_OBJECT(vp)) {
// We need to initialize the class. // We need to initialize the class.
JSClass* c; nsXBLJSClass* c;
void* classObject; void* classObject;
nsStringKey key(aClassName); nsStringKey key(aClassName);
classObject = (nsXBLService::gClassTable)->Get(&key); classObject = (nsXBLService::gClassTable)->Get(&key);
if (classObject) if (classObject) {
c = (JSClass*)classObject; c = NS_STATIC_CAST(nsXBLJSClass*, classObject);
else {
// We need to create a struct for this class. // If c is on the LRU list (i.e., not linked to itself), remove it now!
c = new JSClass; JSCList* link = NS_STATIC_CAST(JSCList*, c);
memset(c, 0, sizeof(JSClass)); if (c->next != link) {
c->name = nsXPIDLCString::Copy(aClassName); JS_REMOVE_AND_INIT_LINK(link);
c->flags = JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS; nsXBLService::gClassLRUListLength--;
c->addProperty = c->delProperty = c->setProperty = c->getProperty = JS_PropertyStub; }
c->enumerate = JS_EnumerateStub; } else {
c->resolve = JS_ResolveStub; if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) {
c->convert = JS_ConvertStub; // We need to create a struct for this class.
c->finalize = XBLFinalize; 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);
nsStringKey oldKey(c->name);
(nsXBLService::gClassTable)->Remove(&oldKey);
// Change the class name and we're done.
nsMemory::Free(c->name);
c->name = nsXPIDLCString::Copy(aClassName);
}
// Add c to our table. // Add c to our table.
(nsXBLService::gClassTable)->Put(&key, (void*)c); (nsXBLService::gClassTable)->Put(&key, (void*)c);
} }
// Retrieve the current prototype for the JS object. // Retrieve the current prototype of the JS object.
parent_proto = JS_GetPrototype(jscontext, object); JSObject* parent_proto = ::JS_GetPrototype(jscontext, object);
proto = JS_InitClass(jscontext, // context
global, // global object // Make a new object prototyped by parent_proto and parented by global.
parent_proto, // parent proto proto = ::JS_InitClass(jscontext, // context
c, // JSClass global, // global object
XBLBindingCtor, // JSNative ctor parent_proto, // parent proto
0, // ctor args c, // JSClass
nsnull, // proto props NULL, // JSNative ctor
nsnull, // proto funcs 0, // ctor args
nsnull, // ctor props (static) nsnull, // proto props
nsnull); // ctor funcs (static) nsnull, // proto funcs
if (nsnull == proto) { nsnull, // ctor props (static)
return NS_ERROR_FAILURE; 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; *aClassObject = (void*)proto;
} }
else if ((nsnull != constructor) && JSVAL_IS_OBJECT(vp)) {
proto = JSVAL_TO_OBJECT(vp);
}
else { else {
return NS_ERROR_FAILURE; proto = JSVAL_TO_OBJECT(vp);
} }
// Set the prototype of our object to be the new class. // Set the prototype of our object to be the new class.
JS_SetPrototype(jscontext, object, proto); ::JS_SetPrototype(jscontext, object, proto);
return NS_OK; return NS_OK;
} }

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

@ -19,7 +19,7 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -674,7 +674,7 @@ nsXBLEventHandler::RemoveEventHandlers()
PRBool mouse = nsXBLBinding::IsMouseHandler(type); PRBool mouse = nsXBLBinding::IsMouseHandler(type);
PRBool key = nsXBLBinding::IsKeyHandler(type); PRBool key = nsXBLBinding::IsKeyHandler(type);
PRBool focus = nsXBLBinding::IsFocusHandler(type); PRBool focus = nsXBLBinding::IsFocusHandler(type);
PRBool xul = nsXBLBinding::IsXULHandler(type); // XXX not used: PRBool xul = nsXBLBinding::IsXULHandler(type);
PRBool scroll = nsXBLBinding::IsScrollHandler(type); PRBool scroll = nsXBLBinding::IsScrollHandler(type);
// Remove the event listener. // Remove the event listener.

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

@ -19,7 +19,7 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -122,6 +122,10 @@ nsINameSpaceManager* nsXBLService::gNameSpaceManager = nsnull;
nsHashtable* nsXBLService::gClassTable = nsnull; nsHashtable* nsXBLService::gClassTable = nsnull;
JSCList nsXBLService::gClassLRUList = JS_INIT_STATIC_CLIST(&nsXBLService::gClassLRUList);
PRUint32 nsXBLService::gClassLRUListLength = 0;
PRUint32 nsXBLService::gClassLRUListQuota = 64;
nsIAtom* nsXBLService::kExtendsAtom = nsnull; nsIAtom* nsXBLService::kExtendsAtom = nsnull;
nsIAtom* nsXBLService::kHasChildrenAtom = nsnull; nsIAtom* nsXBLService::kHasChildrenAtom = nsnull;
nsIAtom* nsXBLService::kURIAtom = nsnull; nsIAtom* nsXBLService::kURIAtom = nsnull;
@ -175,14 +179,6 @@ nsXBLService::nsXBLService(void)
} }
} }
static PRBool PR_CALLBACK DeleteClasses(nsHashKey* aKey, void* aValue, void* closure)
{
JSClass* c = (JSClass*)aValue;
nsMemory::Free(c->name);
delete c;
return PR_TRUE; // return PR_TRUE to continue for nsHashtable enumerator
}
nsXBLService::~nsXBLService(void) nsXBLService::~nsXBLService(void)
{ {
gRefCnt--; gRefCnt--;
@ -197,10 +193,23 @@ nsXBLService::~nsXBLService(void)
NS_RELEASE(kHasChildrenAtom); NS_RELEASE(kHasChildrenAtom);
NS_RELEASE(kURIAtom); NS_RELEASE(kURIAtom);
// Walk the hashtable and delete the JSClasses // Walk the LRU list removing and deleting the nsXBLJSClasses.
if (gClassTable) while (!JS_CLIST_IS_EMPTY(&gClassLRUList)) {
gClassTable->Enumerate(DeleteClasses); JSCList* lru = gClassLRUList.next;
JS_REMOVE_AND_INIT_LINK(lru);
nsXBLJSClass* c = NS_STATIC_CAST(nsXBLJSClass*, lru);
delete c;
}
// Any straggling nsXBLJSClass instances held by unfinalized JS objects
// created for bindings will be deleted when those objects are finalized
// (and not put on gClassLRUList, because length >= quota).
gClassLRUListLength = gClassLRUListQuota = 0;
// At this point, the only hash table entries should be for referenced
// XBL class structs held by unfinalized JS binding objects.
delete gClassTable; delete gClassTable;
gClassTable = nsnull;
} }
} }

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

@ -19,13 +19,16 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
#include "nsIXBLService.h" #include "nsIXBLService.h"
#include "jsapi.h" // nsXBLJSClass derives from JSClass
#include "jsclist.h" // nsXBLJSClass derives from JSCList
class nsIXBLBinding; class nsIXBLBinding;
class nsINameSpaceManager; class nsINameSpaceManager;
class nsIContent; class nsIContent;
@ -36,7 +39,7 @@ class nsIURI;
class nsSupportsHashtable; class nsSupportsHashtable;
class nsHashtable; class nsHashtable;
class nsXBLService: public nsIXBLService class nsXBLService : public nsIXBLService
{ {
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
@ -52,10 +55,10 @@ class nsXBLService: public nsIXBLService
// For a given element, returns a flat list of all the anonymous children that need // For a given element, returns a flat list of all the anonymous children that need
// frames built. // frames built.
NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement, NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement,
PRBool* aMultipleInsertionPoints); PRBool* aMultipleInsertionPoints);
// Gets the object's base class type. // Gets the object's base class type.
NS_IMETHOD ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID, nsIAtom** aResult); NS_IMETHOD ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID, nsIAtom** aResult);
NS_IMETHOD AllowScripts(nsIContent* aContent, PRBool* aAllowScripts); NS_IMETHOD AllowScripts(nsIContent* aContent, PRBool* aAllowScripts);
@ -78,8 +81,8 @@ public:
NS_IMETHOD StripWhitespaceNodes(nsIContent* aContent); NS_IMETHOD StripWhitespaceNodes(nsIContent* aContent);
// MEMBER VARIABLES // MEMBER VARIABLES
public: public:
static nsSupportsHashtable* mBindingTable; // This is a table of all the bindings files static nsSupportsHashtable* mBindingTable; // This is a table of all the bindings files
// we have loaded // we have loaded
// during this session. // during this session.
static nsSupportsHashtable* mScriptAccessTable; // Can the doc's bindings access scripts static nsSupportsHashtable* mScriptAccessTable; // Can the doc's bindings access scripts
@ -89,12 +92,30 @@ public:
static PRUint32 gRefCnt; // A count of XBLservice instances. static PRUint32 gRefCnt; // A count of XBLservice instances.
static PRBool gDisableChromeCache; static PRBool gDisableChromeCache;
static nsHashtable* gClassTable; // A table of JSClass objects. static nsHashtable* gClassTable; // A table of nsXBLJSClass objects.
static JSCList gClassLRUList; // LRU list of cached classes.
static PRUint32 gClassLRUListLength; // Number of classes on LRU list.
static PRUint32 gClassLRUListQuota; // Quota on class LRU list.
// XBL Atoms // XBL Atoms
static nsIAtom* kExtendsAtom; static nsIAtom* kExtendsAtom;
static nsIAtom* kHasChildrenAtom; static nsIAtom* kHasChildrenAtom;
static nsIAtom* kURIAtom; static nsIAtom* kURIAtom;
}; };
class nsXBLJSClass : public JSCList, public JSClass
{
private:
nsrefcnt mRefCnt;
nsrefcnt Destroy();
public:
nsXBLJSClass(const nsCString& aClassName);
~nsXBLJSClass() { nsMemory::Free(name); }
nsrefcnt Hold() { return ++mRefCnt; }
nsrefcnt Drop() { return --mRefCnt ? mRefCnt : Destroy(); }
};

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

@ -19,7 +19,7 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -118,16 +118,43 @@ NS_IMPL_ISUPPORTS1(nsXBLAttributeEntry, nsIXBLAttributeEntry)
PR_STATIC_CALLBACK(void) PR_STATIC_CALLBACK(void)
XBLFinalize(JSContext *cx, JSObject *obj) XBLFinalize(JSContext *cx, JSObject *obj)
{ {
nsJSUtils::nsGenericFinalize(cx, obj); nsXBLJSClass* c = NS_STATIC_CAST(nsXBLJSClass*, ::JS_GetClass(cx, obj));
c->Drop();
} }
// nsXBLJSClass::nsXBLJSClass(const nsCString& aClassName)
// XULElement constructor
//
PR_STATIC_CALLBACK(JSBool)
XBLBindingCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{ {
return JS_FALSE; memset(this, 0, sizeof(nsXBLJSClass));
next = prev = NS_STATIC_CAST(JSCList*, this);
name = nsXPIDLCString::Copy(aClassName);
addProperty = delProperty = setProperty = getProperty = ::JS_PropertyStub;
enumerate = ::JS_EnumerateStub;
resolve = ::JS_ResolveStub;
convert = ::JS_ConvertStub;
finalize = XBLFinalize;
}
nsrefcnt
nsXBLJSClass::Destroy()
{
NS_ASSERTION(next == prev && prev == NS_STATIC_CAST(JSCList*, this),
"referenced nsXBLJSClass is on LRU list already!?");
if (nsXBLService::gClassLRUListLength >= nsXBLService::gClassLRUListQuota) {
// Over LRU list quota, just unhash and delete this class.
if (nsXBLService::gClassTable) {
nsStringKey key(name);
(nsXBLService::gClassTable)->Remove(&key);
}
delete this;
} else {
// Put this most-recently-used class on end of the LRU-sorted freelist.
JSCList* mru = NS_STATIC_CAST(JSCList*, this);
JS_APPEND_LINK(mru, &nsXBLService::gClassLRUList);
nsXBLService::gClassLRUListLength++;
}
return 0;
} }
// Static initialization // Static initialization
@ -216,10 +243,10 @@ NS_IMPL_ISUPPORTS1(nsXBLBinding, nsIXBLBinding)
// Constructors/Destructors // Constructors/Destructors
nsXBLBinding::nsXBLBinding(void) nsXBLBinding::nsXBLBinding(void)
: mAttributeTable(nsnull), : mFirstHandler(nsnull),
mInsertionPointTable(nsnull),
mIsStyleBinding(PR_TRUE), mIsStyleBinding(PR_TRUE),
mFirstHandler(nsnull) mAttributeTable(nsnull),
mInsertionPointTable(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
gRefCnt++; gRefCnt++;
@ -955,7 +982,7 @@ nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, PRInt32 aNameSpaceID, PRBool
if (!aRemoveFlag) { if (!aRemoveFlag) {
// Construct a new text node and insert it. // Construct a new text node and insert it.
nsAutoString value; nsAutoString value;
nsresult result = mBoundElement->GetAttribute(aNameSpaceID, aAttribute, value); mBoundElement->GetAttribute(aNameSpaceID, aAttribute, value);
if (!value.IsEmpty()) { if (!value.IsEmpty()) {
nsCOMPtr<nsIDOMText> textNode; nsCOMPtr<nsIDOMText> textNode;
nsCOMPtr<nsIDocument> doc; nsCOMPtr<nsIDocument> doc;
@ -1018,9 +1045,9 @@ nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocumen
// XXX Sanity check to make sure our class name matches // XXX Sanity check to make sure our class name matches
// Pull ourselves out of the proto chain. // Pull ourselves out of the proto chain.
JSContext* jscontext = (JSContext*)context->GetNativeContext(); JSContext* jscontext = (JSContext*)context->GetNativeContext();
JSObject* ourProto = JS_GetPrototype(jscontext, scriptObject); JSObject* ourProto = ::JS_GetPrototype(jscontext, scriptObject);
JSObject* grandProto = JS_GetPrototype(jscontext, ourProto); JSObject* grandProto = ::JS_GetPrototype(jscontext, ourProto);
JS_SetPrototype(jscontext, scriptObject, grandProto); ::JS_SetPrototype(jscontext, scriptObject, grandProto);
} }
} }
} }
@ -1065,69 +1092,84 @@ nsXBLBinding::InitClass(const nsCString& aClassName, nsIScriptContext* aContext,
// First ensure our JS class is initialized. // First ensure our JS class is initialized.
JSContext* jscontext = (JSContext*)aContext->GetNativeContext(); JSContext* jscontext = (JSContext*)aContext->GetNativeContext();
JSObject* proto = nsnull; JSObject* global = ::JS_GetGlobalObject(jscontext);
JSObject* constructor = nsnull;
JSObject* parent_proto = nsnull;
JSObject* global = JS_GetGlobalObject(jscontext);
jsval vp; jsval vp;
JSObject* proto;
if ((PR_TRUE != JS_LookupProperty(jscontext, global, aClassName, &vp)) || if ((! ::JS_LookupProperty(jscontext, global, aClassName, &vp)) ||
!JSVAL_IS_OBJECT(vp) || JSVAL_IS_PRIMITIVE(vp)) {
((constructor = JSVAL_TO_OBJECT(vp)) == nsnull) ||
(PR_TRUE != JS_LookupProperty(jscontext, JSVAL_TO_OBJECT(vp), "prototype", &vp)) ||
!JSVAL_IS_OBJECT(vp)) {
// We need to initialize the class. // We need to initialize the class.
JSClass* c; nsXBLJSClass* c;
void* classObject; void* classObject;
nsStringKey key(aClassName); nsStringKey key(aClassName);
classObject = (nsXBLService::gClassTable)->Get(&key); classObject = (nsXBLService::gClassTable)->Get(&key);
if (classObject) if (classObject) {
c = (JSClass*)classObject; c = NS_STATIC_CAST(nsXBLJSClass*, classObject);
else {
// We need to create a struct for this class. // If c is on the LRU list (i.e., not linked to itself), remove it now!
c = new JSClass; JSCList* link = NS_STATIC_CAST(JSCList*, c);
memset(c, 0, sizeof(JSClass)); if (c->next != link) {
c->name = nsXPIDLCString::Copy(aClassName); JS_REMOVE_AND_INIT_LINK(link);
c->flags = JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS; nsXBLService::gClassLRUListLength--;
c->addProperty = c->delProperty = c->setProperty = c->getProperty = JS_PropertyStub; }
c->enumerate = JS_EnumerateStub; } else {
c->resolve = JS_ResolveStub; if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) {
c->convert = JS_ConvertStub; // We need to create a struct for this class.
c->finalize = XBLFinalize; 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);
nsStringKey oldKey(c->name);
(nsXBLService::gClassTable)->Remove(&oldKey);
// Change the class name and we're done.
nsMemory::Free(c->name);
c->name = nsXPIDLCString::Copy(aClassName);
}
// Add c to our table. // Add c to our table.
(nsXBLService::gClassTable)->Put(&key, (void*)c); (nsXBLService::gClassTable)->Put(&key, (void*)c);
} }
// Retrieve the current prototype for the JS object. // Retrieve the current prototype of the JS object.
parent_proto = JS_GetPrototype(jscontext, object); JSObject* parent_proto = ::JS_GetPrototype(jscontext, object);
proto = JS_InitClass(jscontext, // context
global, // global object // Make a new object prototyped by parent_proto and parented by global.
parent_proto, // parent proto proto = ::JS_InitClass(jscontext, // context
c, // JSClass global, // global object
XBLBindingCtor, // JSNative ctor parent_proto, // parent proto
0, // ctor args c, // JSClass
nsnull, // proto props NULL, // JSNative ctor
nsnull, // proto funcs 0, // ctor args
nsnull, // ctor props (static) nsnull, // proto props
nsnull); // ctor funcs (static) nsnull, // proto funcs
if (nsnull == proto) { nsnull, // ctor props (static)
return NS_ERROR_FAILURE; 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; *aClassObject = (void*)proto;
} }
else if ((nsnull != constructor) && JSVAL_IS_OBJECT(vp)) {
proto = JSVAL_TO_OBJECT(vp);
}
else { else {
return NS_ERROR_FAILURE; proto = JSVAL_TO_OBJECT(vp);
} }
// Set the prototype of our object to be the new class. // Set the prototype of our object to be the new class.
JS_SetPrototype(jscontext, object, proto); ::JS_SetPrototype(jscontext, object, proto);
return NS_OK; return NS_OK;
} }

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

@ -19,7 +19,7 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -674,7 +674,7 @@ nsXBLEventHandler::RemoveEventHandlers()
PRBool mouse = nsXBLBinding::IsMouseHandler(type); PRBool mouse = nsXBLBinding::IsMouseHandler(type);
PRBool key = nsXBLBinding::IsKeyHandler(type); PRBool key = nsXBLBinding::IsKeyHandler(type);
PRBool focus = nsXBLBinding::IsFocusHandler(type); PRBool focus = nsXBLBinding::IsFocusHandler(type);
PRBool xul = nsXBLBinding::IsXULHandler(type); // XXX not used: PRBool xul = nsXBLBinding::IsXULHandler(type);
PRBool scroll = nsXBLBinding::IsScrollHandler(type); PRBool scroll = nsXBLBinding::IsScrollHandler(type);
// Remove the event listener. // Remove the event listener.

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

@ -19,7 +19,7 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
@ -122,6 +122,10 @@ nsINameSpaceManager* nsXBLService::gNameSpaceManager = nsnull;
nsHashtable* nsXBLService::gClassTable = nsnull; nsHashtable* nsXBLService::gClassTable = nsnull;
JSCList nsXBLService::gClassLRUList = JS_INIT_STATIC_CLIST(&nsXBLService::gClassLRUList);
PRUint32 nsXBLService::gClassLRUListLength = 0;
PRUint32 nsXBLService::gClassLRUListQuota = 64;
nsIAtom* nsXBLService::kExtendsAtom = nsnull; nsIAtom* nsXBLService::kExtendsAtom = nsnull;
nsIAtom* nsXBLService::kHasChildrenAtom = nsnull; nsIAtom* nsXBLService::kHasChildrenAtom = nsnull;
nsIAtom* nsXBLService::kURIAtom = nsnull; nsIAtom* nsXBLService::kURIAtom = nsnull;
@ -175,14 +179,6 @@ nsXBLService::nsXBLService(void)
} }
} }
static PRBool PR_CALLBACK DeleteClasses(nsHashKey* aKey, void* aValue, void* closure)
{
JSClass* c = (JSClass*)aValue;
nsMemory::Free(c->name);
delete c;
return PR_TRUE; // return PR_TRUE to continue for nsHashtable enumerator
}
nsXBLService::~nsXBLService(void) nsXBLService::~nsXBLService(void)
{ {
gRefCnt--; gRefCnt--;
@ -197,10 +193,23 @@ nsXBLService::~nsXBLService(void)
NS_RELEASE(kHasChildrenAtom); NS_RELEASE(kHasChildrenAtom);
NS_RELEASE(kURIAtom); NS_RELEASE(kURIAtom);
// Walk the hashtable and delete the JSClasses // Walk the LRU list removing and deleting the nsXBLJSClasses.
if (gClassTable) while (!JS_CLIST_IS_EMPTY(&gClassLRUList)) {
gClassTable->Enumerate(DeleteClasses); JSCList* lru = gClassLRUList.next;
JS_REMOVE_AND_INIT_LINK(lru);
nsXBLJSClass* c = NS_STATIC_CAST(nsXBLJSClass*, lru);
delete c;
}
// Any straggling nsXBLJSClass instances held by unfinalized JS objects
// created for bindings will be deleted when those objects are finalized
// (and not put on gClassLRUList, because length >= quota).
gClassLRUListLength = gClassLRUListQuota = 0;
// At this point, the only hash table entries should be for referenced
// XBL class structs held by unfinalized JS binding objects.
delete gClassTable; delete gClassTable;
gClassTable = nsnull;
} }
} }

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

@ -19,13 +19,16 @@
* *
* Original Author: David W. Hyatt (hyatt@netscape.com) * Original Author: David W. Hyatt (hyatt@netscape.com)
* *
* Contributor(s): * Contributor(s): Brendan Eich (brendan@mozilla.org)
*/ */
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
#include "nsIXBLService.h" #include "nsIXBLService.h"
#include "jsapi.h" // nsXBLJSClass derives from JSClass
#include "jsclist.h" // nsXBLJSClass derives from JSCList
class nsIXBLBinding; class nsIXBLBinding;
class nsINameSpaceManager; class nsINameSpaceManager;
class nsIContent; class nsIContent;
@ -36,7 +39,7 @@ class nsIURI;
class nsSupportsHashtable; class nsSupportsHashtable;
class nsHashtable; class nsHashtable;
class nsXBLService: public nsIXBLService class nsXBLService : public nsIXBLService
{ {
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
@ -52,10 +55,10 @@ class nsXBLService: public nsIXBLService
// For a given element, returns a flat list of all the anonymous children that need // For a given element, returns a flat list of all the anonymous children that need
// frames built. // frames built.
NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement, NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement,
PRBool* aMultipleInsertionPoints); PRBool* aMultipleInsertionPoints);
// Gets the object's base class type. // Gets the object's base class type.
NS_IMETHOD ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID, nsIAtom** aResult); NS_IMETHOD ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID, nsIAtom** aResult);
NS_IMETHOD AllowScripts(nsIContent* aContent, PRBool* aAllowScripts); NS_IMETHOD AllowScripts(nsIContent* aContent, PRBool* aAllowScripts);
@ -78,8 +81,8 @@ public:
NS_IMETHOD StripWhitespaceNodes(nsIContent* aContent); NS_IMETHOD StripWhitespaceNodes(nsIContent* aContent);
// MEMBER VARIABLES // MEMBER VARIABLES
public: public:
static nsSupportsHashtable* mBindingTable; // This is a table of all the bindings files static nsSupportsHashtable* mBindingTable; // This is a table of all the bindings files
// we have loaded // we have loaded
// during this session. // during this session.
static nsSupportsHashtable* mScriptAccessTable; // Can the doc's bindings access scripts static nsSupportsHashtable* mScriptAccessTable; // Can the doc's bindings access scripts
@ -89,12 +92,30 @@ public:
static PRUint32 gRefCnt; // A count of XBLservice instances. static PRUint32 gRefCnt; // A count of XBLservice instances.
static PRBool gDisableChromeCache; static PRBool gDisableChromeCache;
static nsHashtable* gClassTable; // A table of JSClass objects. static nsHashtable* gClassTable; // A table of nsXBLJSClass objects.
static JSCList gClassLRUList; // LRU list of cached classes.
static PRUint32 gClassLRUListLength; // Number of classes on LRU list.
static PRUint32 gClassLRUListQuota; // Quota on class LRU list.
// XBL Atoms // XBL Atoms
static nsIAtom* kExtendsAtom; static nsIAtom* kExtendsAtom;
static nsIAtom* kHasChildrenAtom; static nsIAtom* kHasChildrenAtom;
static nsIAtom* kURIAtom; static nsIAtom* kURIAtom;
}; };
class nsXBLJSClass : public JSCList, public JSClass
{
private:
nsrefcnt mRefCnt;
nsrefcnt Destroy();
public:
nsXBLJSClass(const nsCString& aClassName);
~nsXBLJSClass() { nsMemory::Free(name); }
nsrefcnt Hold() { return ++mRefCnt; }
nsrefcnt Drop() { return --mRefCnt ? mRefCnt : Destroy(); }
};