зеркало из https://github.com/mozilla/pjs.git
Bug 453929. Cache getElementsBy(Class)Name return values. r=jst
This commit is contained in:
Родитель
f389fed916
Коммит
3421145f67
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче