Bug 453929. Cache getElementsBy(Class)Name return values. r=jst

This commit is contained in:
Boris Zbarsky 2010-02-09 12:09:06 -05:00
Родитель f389fed916
Коммит 3421145f67
4 изменённых файлов: 212 добавлений и 11 удалений

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

@ -296,6 +296,109 @@ NS_GetContentList(nsINode* aRootNode, nsIAtom* aMatchAtom,
return list;
}
// Hashtable for storing nsCacheableFuncStringContentList
static PLDHashTable gFuncStringContentListHashTable;
struct FuncStringContentListHashEntry : public PLDHashEntryHdr
{
nsCacheableFuncStringContentList* mContentList;
};
static PLDHashNumber
FuncStringContentListHashtableHashKey(PLDHashTable *table, const void *key)
{
const nsFuncStringCacheKey* funcStringKey =
static_cast<const nsFuncStringCacheKey *>(key);
return funcStringKey->GetHash();
}
static PRBool
FuncStringContentListHashtableMatchEntry(PLDHashTable *table,
const PLDHashEntryHdr *entry,
const void *key)
{
const FuncStringContentListHashEntry *e =
static_cast<const FuncStringContentListHashEntry *>(entry);
const nsFuncStringCacheKey* ourKey =
static_cast<const nsFuncStringCacheKey *>(key);
return e->mContentList->Equals(ourKey);
}
already_AddRefed<nsContentList>
NS_GetFuncStringContentList(nsINode* aRootNode,
nsContentListMatchFunc aFunc,
nsContentListDestroyFunc aDestroyFunc,
void* aData,
const nsAString& aString)
{
NS_ASSERTION(aRootNode, "content list has to have a root");
nsCacheableFuncStringContentList* list = nsnull;
static PLDHashTableOps hash_table_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
FuncStringContentListHashtableHashKey,
FuncStringContentListHashtableMatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
PL_DHashFinalizeStub
};
// Initialize the hashtable if needed.
if (!gFuncStringContentListHashTable.ops) {
PRBool success = PL_DHashTableInit(&gFuncStringContentListHashTable,
&hash_table_ops, nsnull,
sizeof(FuncStringContentListHashEntry),
16);
if (!success) {
gFuncStringContentListHashTable.ops = nsnull;
}
}
FuncStringContentListHashEntry *entry = nsnull;
// First we look in our hashtable. Then we create a content list if needed
if (gFuncStringContentListHashTable.ops) {
nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
// A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
// when the entry is already in the hashtable.
entry = static_cast<FuncStringContentListHashEntry *>
(PL_DHashTableOperate(&gFuncStringContentListHashTable,
&hashKey,
PL_DHASH_ADD));
if (entry)
list = entry->mContentList;
}
if (!list) {
// We need to create a ContentList and add it to our new entry, if
// we have an entry
list = new nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc, aData, aString);
if (entry) {
if (list)
entry->mContentList = list;
else
PL_DHashTableRawRemove(&gContentListHashTable, entry);
}
NS_ENSURE_TRUE(list, nsnull);
} else {
// List was already in the hashtable; clean up our new aData
if (aDestroyFunc) {
(*aDestroyFunc)(aData);
}
}
NS_ADDREF(list);
// Don't cache these lists globally
return list;
}
// nsContentList implementation
@ -450,7 +553,7 @@ nsContentList::NodeWillBeDestroyed(const nsINode* aNode)
{
// We shouldn't do anything useful from now on
RemoveFromHashtable();
RemoveFromCaches();
mRootNode = nsnull;
// We will get no more updates, so we can never know we're up to
@ -913,6 +1016,29 @@ nsContentList::BringSelfUpToDate(PRBool aDoFlush)
"PopulateSelf dod not bring content list up to date!");
}
nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList()
{
RemoveFromFuncStringHashtable();
}
void
nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable()
{
if (!gFuncStringContentListHashTable.ops) {
return;
}
nsFuncStringCacheKey key(mRootNode, mFunc, mString);
PL_DHashTableOperate(&gFuncStringContentListHashTable,
&key,
PL_DHASH_REMOVE);
if (gFuncStringContentListHashTable.entryCount == 0) {
PL_DHashTableFinish(&gFuncStringContentListHashTable);
gFuncStringContentListHashTable.ops = nsnull;
}
}
#ifdef DEBUG_CONTENT_LIST
void
nsContentList::AssertInSync()

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

@ -55,6 +55,7 @@
#include "nsINameSpaceManager.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsCRT.h"
// Magic namespace id that means "match all namespaces". This is
// negative so it won't collide with actual namespace constants.
@ -396,6 +397,15 @@ protected:
Reset();
}
/**
* To be called from non-destructor locations that want to remove from caches.
* Needed because if subclasses want to have cache behavior they can't just
* override RemoveFromHashtable(), since we call that in our destructor.
*/
virtual void RemoveFromCaches() {
RemoveFromHashtable();
}
/**
* Function to use to determine whether a piece of content matches
* our criterion
@ -434,8 +444,69 @@ protected:
#endif
};
/**
* A class of cacheable content list; cached on the combination of aRootNode + aFunc + aDataString
*/
class nsCacheableFuncStringContentList;
class NS_STACK_CLASS nsFuncStringCacheKey {
public:
nsFuncStringCacheKey(nsINode* aRootNode,
nsContentListMatchFunc aFunc,
const nsAString& aString) :
mRootNode(aRootNode),
mFunc(aFunc),
mString(aString)
{}
PRUint32 GetHash(void) const
{
return NS_PTR_TO_INT32(mRootNode) ^ (NS_PTR_TO_INT32(mFunc) << 12) ^
nsCRT::HashCode(PromiseFlatString(mString).get());
}
private:
friend class nsCacheableFuncStringContentList;
nsINode* const mRootNode;
const nsContentListMatchFunc mFunc;
const nsAString& mString;
};
class nsCacheableFuncStringContentList : public nsContentList {
public:
nsCacheableFuncStringContentList(nsINode* aRootNode,
nsContentListMatchFunc aFunc,
nsContentListDestroyFunc aDestroyFunc,
void* aData,
const nsAString& aString) :
nsContentList(aRootNode, aFunc, aDestroyFunc, aData),
mString(aString)
{}
virtual ~nsCacheableFuncStringContentList();
PRBool Equals(const nsFuncStringCacheKey* aKey) {
return mRootNode == aKey->mRootNode && mFunc == aKey->mFunc &&
mString == aKey->mString;
}
protected:
virtual void RemoveFromCaches() {
RemoveFromFuncStringHashtable();
}
void RemoveFromFuncStringHashtable();
nsString mString;
};
already_AddRefed<nsContentList>
NS_GetContentList(nsINode* aRootNode, nsIAtom* aMatchAtom,
PRInt32 aMatchNameSpaceId);
already_AddRefed<nsContentList>
NS_GetFuncStringContentList(nsINode* aRootNode,
nsContentListMatchFunc aFunc,
nsContentListDestroyFunc aDestroyFunc,
void* aData,
const nsAString& aString);
#endif // nsContentList_h___

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

@ -2861,21 +2861,24 @@ nsDocument::GetElementsByClassNameHelper(nsINode* aRootNode,
aRootNode->GetOwnerDoc()->GetCompatibilityMode() ==
eCompatibility_NavQuirks ?
eIgnoreCase : eCaseMatters;
elements = new nsContentList(aRootNode, MatchClassNames,
DestroyClassNameArray, info);
elements =
NS_GetFuncStringContentList(aRootNode, MatchClassNames,
DestroyClassNameArray, info,
aClasses).get();
} else {
delete info;
info = nsnull;
elements = new nsBaseContentList();
NS_IF_ADDREF(elements);
}
if (!elements) {
delete info;
return NS_ERROR_OUT_OF_MEMORY;
}
// Transfer ownership
*aReturn = elements;
NS_ADDREF(*aReturn);
return NS_OK;
}

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

@ -2280,17 +2280,18 @@ NS_IMETHODIMP
nsHTMLDocument::GetElementsByName(const nsAString& aElementName,
nsIDOMNodeList** aReturn)
{
void* elementNameData = new nsString(aElementName);
nsString* elementNameData = new nsString(aElementName);
NS_ENSURE_TRUE(elementNameData, NS_ERROR_OUT_OF_MEMORY);
nsContentList* elements =
new nsContentList(this,
MatchNameAttribute,
nsContentUtils::DestroyMatchString,
elementNameData);
NS_GetFuncStringContentList(this,
MatchNameAttribute,
nsContentUtils::DestroyMatchString,
elementNameData,
*elementNameData).get();
NS_ENSURE_TRUE(elements, NS_ERROR_OUT_OF_MEMORY);
// Transfer ownership
*aReturn = elements;
NS_ADDREF(*aReturn);
return NS_OK;
}