diff --git a/mozilla/content/base/src/nsContentList.cpp b/mozilla/content/base/src/nsContentList.cpp index e6088561fba..7f52caadcc6 100644 --- a/mozilla/content/base/src/nsContentList.cpp +++ b/mozilla/content/base/src/nsContentList.cpp @@ -52,6 +52,8 @@ #include "nsIDOMHTMLFormElement.h" #include "nsIContentList.h" +#include "pldhash.h" + nsBaseContentList::nsBaseContentList() { NS_INIT_REFCNT(); @@ -272,15 +274,117 @@ nsFormContentList::Reset() return nsBaseContentList::Reset(); } +// Hashtable for storing nsContentLists +static PLDHashTable gContentListHashTable; + +struct ContentListHashEntry : public PLDHashEntryHdr +{ + nsContentList* mContentList; +}; + +PR_STATIC_CALLBACK(const void *) +ContentListHashtableGetKey(PLDHashTable *table, PLDHashEntryHdr *entry) +{ + ContentListHashEntry *e = NS_STATIC_CAST(ContentListHashEntry *, entry); + return e->mContentList->GetKey(); +} + +PR_STATIC_CALLBACK(PLDHashNumber) +ContentListHashtableHashKey(PLDHashTable *table, const void *key) +{ + const nsContentListKey* list = NS_STATIC_CAST(const nsContentListKey *, key); + return list->GetHash(); +} + +PR_STATIC_CALLBACK(PRBool) +ContentListHashtableMatchEntry(PLDHashTable *table, + const PLDHashEntryHdr *entry, + const void *key) +{ + const ContentListHashEntry *e = + NS_STATIC_CAST(const ContentListHashEntry *, entry); + const nsContentListKey* list1 = e->mContentList->GetKey(); + const nsContentListKey* list2 = NS_STATIC_CAST(const nsContentListKey *, key); + + return list1->Equals(*list2); +} + +nsresult +NS_GetContentList(nsIDocument* aDocument, nsIAtom* aMatchAtom, + PRInt32 aMatchNameSpaceId, nsIContent* aRootContent, + nsIContentList** aInstancePtrResult) +{ + *aInstancePtrResult = nsnull; + nsContentList* list = nsnull; + + static PLDHashTableOps hash_table_ops = + { + PL_DHashAllocTable, + PL_DHashFreeTable, + ContentListHashtableGetKey, + ContentListHashtableHashKey, + ContentListHashtableMatchEntry, + PL_DHashMoveEntryStub, + PL_DHashClearEntryStub, + PL_DHashFinalizeStub + }; + + // Initialize the hashtable if needed. + if (!gContentListHashTable.ops) { + PRBool success = PL_DHashTableInit(&gContentListHashTable, + &hash_table_ops, nsnull, + sizeof(ContentListHashEntry), + 16); + + if (!success) { + gContentListHashTable.ops = nsnull; + } + } + + ContentListHashEntry *entry = nsnull; + // First we look in our hashtable. Then we create a content list if needed + if (gContentListHashTable.ops) { + nsContentListKey hashKey(aDocument, aMatchAtom, + aMatchNameSpaceId, aRootContent); + + // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases + // when the entry is already in the hashtable. + entry = NS_STATIC_CAST(ContentListHashEntry *, + PL_DHashTableOperate(&gContentListHashTable, + &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 nsContentList(aDocument, aMatchAtom, + aMatchNameSpaceId, aRootContent); + if (entry) { + if (list) + entry->mContentList = list; + else + PL_DHashTableRawRemove(&gContentListHashTable, entry); + } + } + + NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); + + *aInstancePtrResult = list; + NS_ADDREF(*aInstancePtrResult); + + return NS_OK; +} + // nsContentList implementation nsContentList::nsContentList(const nsContentList& aContentList) - : nsBaseContentList() + : nsBaseContentList(), nsContentListKey(aContentList) { mFunc = aContentList.mFunc; - mMatchAtom = aContentList.mMatchAtom; - mDocument = aContentList.mDocument; if (aContentList.mData) { mData = new nsString(*aContentList.mData); @@ -289,39 +393,31 @@ nsContentList::nsContentList(const nsContentList& aContentList) } mMatchAll = aContentList.mMatchAll; - mRootContent = aContentList.mRootContent; mElements = aContentList.mElements; } nsContentList::nsContentList(nsIDocument *aDocument) - : nsBaseContentList() + : nsBaseContentList(), nsContentListKey(aDocument, nsnull, kNameSpaceID_Unknown, nsnull) { mFunc = nsnull; - mMatchAtom = nsnull; - mDocument = aDocument; mData = nsnull; mMatchAll = PR_FALSE; - mRootContent = nsnull; } nsContentList::nsContentList(nsIDocument *aDocument, nsIAtom* aMatchAtom, PRInt32 aMatchNameSpaceId, nsIContent* aRootContent) - : nsBaseContentList() + : nsBaseContentList(), nsContentListKey(aDocument, aMatchAtom, aMatchNameSpaceId, aRootContent) { - mMatchAtom = aMatchAtom; - NS_IF_ADDREF(mMatchAtom); if (nsLayoutAtoms::wildcard == mMatchAtom) { mMatchAll = PR_TRUE; } else { mMatchAll = PR_FALSE; } - mMatchNameSpaceId = aMatchNameSpaceId; mFunc = nsnull; mData = nsnull; - mRootContent = aRootContent; Init(aDocument); } @@ -329,7 +425,7 @@ nsContentList::nsContentList(nsIDocument *aDocument, nsContentListMatchFunc aFunc, const nsAString& aData, nsIContent* aRootContent) - : nsBaseContentList() + : nsBaseContentList(), nsContentListKey(aDocument, nsnull, kNameSpaceID_Unknown, aRootContent) { mFunc = aFunc; if (!aData.IsEmpty()) { @@ -361,12 +457,11 @@ void nsContentList::Init(nsIDocument *aDocument) nsContentList::~nsContentList() { + RemoveFromHashtable(); if (mDocument) { mDocument->RemoveObserver(this); } - NS_IF_RELEASE(mMatchAtom); - delete mData; } @@ -567,6 +662,8 @@ NS_IMETHODIMP nsContentList::DocumentWillBeDestroyed(nsIDocument *aDocument) { if (mDocument) { + // Our key will change... Best remove ourselves before that happens. + RemoveFromHashtable(); aDocument->RemoveObserver(this); mDocument = nsnull; } @@ -653,14 +750,12 @@ nsContentList::MatchSelf(nsIContent *aContent) } aContent->ChildCount(count); + nsCOMPtr child; for (i = 0; i < count; i++) { - nsIContent *child; - aContent->ChildAt(i, child); + aContent->ChildAt(i, *getter_AddRefs(child)); if (MatchSelf(child)) { - NS_RELEASE(child); return PR_TRUE; } - NS_RELEASE(child); } return PR_FALSE; @@ -681,11 +776,10 @@ nsContentList::PopulateWith(nsIContent *aContent, PRBool aIncludeRoot) } aContent->ChildCount(count); + nsCOMPtr child; for (i = 0; i < count; i++) { - nsIContent *child; - aContent->ChildAt(i, child); + aContent->ChildAt(i, *getter_AddRefs(child)); PopulateWith(child, PR_TRUE); - NS_RELEASE(child); } } @@ -698,11 +792,10 @@ nsContentList::PopulateSelf() PopulateWith(mRootContent, PR_FALSE); } else if (mDocument) { - nsIContent *root = nsnull; - mDocument->GetRootContent(&root); + nsCOMPtr root; + mDocument->GetRootContent(getter_AddRefs(root)); if (root) { PopulateWith(root, PR_TRUE); - NS_RELEASE(root); } } } @@ -769,7 +862,26 @@ void nsContentList::DisconnectFromDocument() { if (mDocument) { + // Our key will change... Best remove ourselves before that happens. + RemoveFromHashtable(); mDocument->RemoveObserver(this); mDocument = nsnull; } } + +void +nsContentList::RemoveFromHashtable() +{ + if (!gContentListHashTable.ops) + return; + + PL_DHashTableOperate(&gContentListHashTable, + GetKey(), + PL_DHASH_REMOVE); + + if (gContentListHashTable.entryCount == 0) { + PL_DHashTableFinish(&gContentListHashTable); + gContentListHashTable.ops = nsnull; + } +} + diff --git a/mozilla/content/base/src/nsContentList.h b/mozilla/content/base/src/nsContentList.h index 787dd649c0c..13db1861d58 100644 --- a/mozilla/content/base/src/nsContentList.h +++ b/mozilla/content/base/src/nsContentList.h @@ -90,7 +90,54 @@ public: NS_IMETHOD Reset(); }; +class nsContentListKey +{ +public: + nsContentListKey(nsIDocument *aDocument, + nsIAtom* aMatchAtom, + PRInt32 aMatchNameSpaceId, + nsIContent* aRootContent) + : mMatchAtom(aMatchAtom), + mMatchNameSpaceId(aMatchNameSpaceId), + mDocument(aDocument), + mRootContent(aRootContent) + { + } + + nsContentListKey(const nsContentListKey& aContentListKey) + : mMatchAtom(aContentListKey.mMatchAtom), + mMatchNameSpaceId(aContentListKey.mMatchNameSpaceId), + mDocument(aContentListKey.mDocument), + mRootContent(aContentListKey.mRootContent) + { + } + + PRBool Equals(const nsContentListKey& aContentListKey) const + { + return + mMatchAtom == aContentListKey.mMatchAtom && + mMatchNameSpaceId == aContentListKey.mMatchNameSpaceId && + mDocument == aContentListKey.mDocument && + mRootContent == aContentListKey.mRootContent; + } + inline PRUint32 GetHash(void) const + { + return + NS_PTR_TO_INT32(mMatchAtom.get()) ^ + (NS_PTR_TO_INT32(mRootContent) << 8) ^ + (NS_PTR_TO_INT32(mDocument) << 16) ^ + (mMatchNameSpaceId << 24); + } + +protected: + nsCOMPtr mMatchAtom; + PRInt32 mMatchNameSpaceId; + nsIDocument* mDocument; // Weak ref + nsIContent* mRootContent; // Weak ref +}; + class nsContentList : public nsBaseContentList, + protected nsContentListKey, public nsIDOMHTMLCollection, public nsIDocumentObserver, public nsIContentList @@ -181,6 +228,11 @@ public: nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); + // Other public methods + nsContentListKey* GetKey() { + return NS_STATIC_CAST(nsContentListKey*, this); + } + protected: nsresult Match(nsIContent *aContent, PRBool *aMatch); void Init(nsIDocument *aDocument); @@ -191,14 +243,16 @@ protected: PRBool IsDescendantOfRoot(nsIContent* aContainer); PRBool ContainsRoot(nsIContent* aContent); nsresult CheckDocumentExistence(); + void RemoveFromHashtable(); - nsIAtom* mMatchAtom; - PRInt32 mMatchNameSpaceId; nsContentListMatchFunc mFunc; nsString* mData; - nsIDocument* mDocument; - nsIContent* mRootContent; PRBool mMatchAll; }; +extern nsresult +NS_GetContentList(nsIDocument* aDocument, nsIAtom* aMatchAtom, + PRInt32 aMatchNameSpaceId, nsIContent* aRootContent, + nsIContentList** aInstancePtrResult); + #endif // nsContentList_h___ diff --git a/mozilla/content/base/src/nsDocument.cpp b/mozilla/content/base/src/nsDocument.cpp index d2f2ac99f8d..35f79ac452e 100644 --- a/mozilla/content/base/src/nsDocument.cpp +++ b/mozilla/content/base/src/nsDocument.cpp @@ -2444,10 +2444,12 @@ nsDocument::GetElementsByTagName(const nsAString& aTagname, { nsCOMPtr nameAtom(dont_AddRef(NS_NewAtom(aTagname))); - nsContentList* list = new nsContentList(this, nameAtom, kNameSpaceID_Unknown); + nsCOMPtr list; + NS_GetContentList(this, nameAtom, kNameSpaceID_Unknown, nsnull, + getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); - return list->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void **)aReturn); + return CallQueryInterface(list, aReturn); } NS_IMETHODIMP @@ -2458,25 +2460,27 @@ nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, PRInt32 nameSpaceId = kNameSpaceID_Unknown; - nsContentList* list = nsnull; + nsCOMPtr list; if (!aNamespaceURI.Equals(NS_LITERAL_STRING("*"))) { mNameSpaceManager->GetNameSpaceID(aNamespaceURI, nameSpaceId); if (nameSpaceId == kNameSpaceID_Unknown) { // Unkonwn namespace means no matches, we create an empty list... - list = new nsContentList(this, nsnull, kNameSpaceID_None); + NS_GetContentList(this, nsnull, kNameSpaceID_None, nsnull, + getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } } if (!list) { nsCOMPtr nameAtom(dont_AddRef(NS_NewAtom(aLocalName))); - list = new nsContentList(this, nameAtom, nameSpaceId); + NS_GetContentList(this, nameAtom, nameSpaceId, nsnull, + getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } - return list->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void **)aReturn); + return CallQueryInterface(list, aReturn); } NS_IMETHODIMP diff --git a/mozilla/content/base/src/nsGenericElement.cpp b/mozilla/content/base/src/nsGenericElement.cpp index fcb1edd318e..98e587c2215 100644 --- a/mozilla/content/base/src/nsGenericElement.cpp +++ b/mozilla/content/base/src/nsGenericElement.cpp @@ -1075,16 +1075,15 @@ nsGenericElement::GetElementsByTagName(const nsAString& aTagname, nameAtom = dont_AddRef(NS_NewAtom(aTagname)); - nsContentList* list = new nsContentList(mDocument, - nameAtom, - kNameSpaceID_Unknown, - this); + nsCOMPtr list; + NS_GetContentList(mDocument, nameAtom, kNameSpaceID_Unknown, this, + getter_AddRefs(list)); if (!list) { return NS_ERROR_OUT_OF_MEMORY; } - return list->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void **)aReturn); + return CallQueryInterface(list, aReturn); } nsresult @@ -1224,7 +1223,7 @@ nsGenericElement::GetElementsByTagNameNS(const nsAString& aNamespaceURI, nsCOMPtr nameAtom(dont_AddRef(NS_NewAtom(aLocalName))); PRInt32 nameSpaceId = kNameSpaceID_Unknown; - nsContentList* list = nsnull; + nsCOMPtr list; if (!aNamespaceURI.Equals(NS_LITERAL_STRING("*"))) { nsCOMPtr nimgr; @@ -1238,18 +1237,20 @@ nsGenericElement::GetElementsByTagNameNS(const nsAString& aNamespaceURI, nsmgr->GetNameSpaceID(aNamespaceURI, nameSpaceId); if (nameSpaceId == kNameSpaceID_Unknown) { - // Unkonwn namespace means no matches, we create an empty list... - list = new nsContentList(mDocument, nsnull, kNameSpaceID_None); + // Unknown namespace means no matches, we create an empty list... + NS_GetContentList(mDocument, nsnull, kNameSpaceID_None, nsnull, + getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } } if (!list) { - list = new nsContentList(mDocument, nameAtom, nameSpaceId, this); + NS_GetContentList(mDocument, nameAtom, nameSpaceId, this, + getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } - return list->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void **)aReturn); + return CallQueryInterface(list, aReturn); } nsresult