diff --git a/mozilla/content/html/document/src/nsHTMLDocument.cpp b/mozilla/content/html/document/src/nsHTMLDocument.cpp index 24cf5af9168..76751b192a5 100644 --- a/mozilla/content/html/document/src/nsHTMLDocument.cpp +++ b/mozilla/content/html/document/src/nsHTMLDocument.cpp @@ -144,8 +144,10 @@ const PRInt32 kBackward = 1; //#define DEBUG_charset - -#define ID_NOT_IN_DOCUMENT ((nsIContent *)1) +// Entries in an nsSmallVoidArray must not have the low bit set, so +// can't use "1" for ID_NOT_IN_DOCUMENT. "2" should be a perfectly +// reasonable value, though -- no real nsIContent* will be equal to 2. +#define ID_NOT_IN_DOCUMENT ((nsIContent *)2) #define NAME_NOT_VALID ((nsBaseContentList*)1) static NS_DEFINE_CID(kCookieServiceCID, NS_COOKIESERVICE_CID); @@ -205,22 +207,80 @@ class IdAndNameMapEntry : public PLDHashEntryHdr { public: IdAndNameMapEntry(const nsAString& aString) : - mKey(aString), mIdContent(nsnull), mContentList(nsnull) + mKey(aString), mNameContentList(nsnull), mIdContentList() { } ~IdAndNameMapEntry() { - if (mContentList && mContentList != NAME_NOT_VALID) { - NS_RELEASE(mContentList); + if (mNameContentList && mNameContentList != NAME_NOT_VALID) { + NS_RELEASE(mNameContentList); } } + nsIContent* GetIdContent() { + return NS_STATIC_CAST(nsIContent*, mIdContentList.SafeElementAt(0)); + } + + // If aForce is true, the new content will become the new content + // returned for this ID, even if there is already such content. + PRBool AddIdContent(nsIContent* aContent, PRBool aForce); + + PRBool RemoveIdContent(nsIContent* aContent) { + // XXXbz should this ever Compact() I guess when all the content is gone + // we'll just get cleaned up in the natural order of things... + return mIdContentList.RemoveElement(aContent) && + mIdContentList.Count() == 0; + } + + void FlagIDNotInDocument() { + NS_ASSERTION(mIdContentList.Count() == 0, + "Flagging ID not in document when we have content?"); + // Note that if this fails that's OK; this is just an optimization + mIdContentList.AppendElement(ID_NOT_IN_DOCUMENT); + } + nsString mKey; - nsIContent *mIdContent; - nsBaseContentList *mContentList; + nsBaseContentList *mNameContentList; +private: + nsSmallVoidArray mIdContentList; }; +PRBool +IdAndNameMapEntry::AddIdContent(nsIContent* aContent, PRBool aForce) +{ + NS_PRECONDITION(aContent, "Must have content"); + NS_PRECONDITION(mIdContentList.IndexOf(nsnull) == -1, + "Why is null in our list?"); + NS_PRECONDITION(aContent != ID_NOT_IN_DOCUMENT, + "Bogus content pointer"); + + if (GetIdContent() == ID_NOT_IN_DOCUMENT) { + NS_ASSERTION(mIdContentList.Count() == 1, "Bogus count"); + return mIdContentList.ReplaceElementAt(aContent, 0); + } + + // Have to check whether it's in the list already, in case we're + // registering from the top when going live. + PRInt32 index = mIdContentList.IndexOf(aContent); + if (index != -1) { + if (!aForce) { + // nothing else to do here + return PR_TRUE; + } + + // Remove it from index + if (NS_UNLIKELY(!mIdContentList.RemoveElementAt(index))) { + return PR_FALSE; + } + + // Fall through to readd at 0 + } + + return aForce ? mIdContentList.InsertElementAt(aContent, 0) : + mIdContentList.AppendElement(aContent); +} + PR_STATIC_CALLBACK(const void *) IdAndNameHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry) @@ -2330,13 +2390,13 @@ nsHTMLDocument::GetElementById(const nsAString& aElementId, PL_DHASH_ADD)); NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); - nsIContent *e = entry->mIdContent; + nsIContent *e = entry->GetIdContent(); - if (e == ID_NOT_IN_DOCUMENT) { + if (!e || e == ID_NOT_IN_DOCUMENT) { // Now we have to flush. It could be that we have a cached "not in - // document" but more content has been added to the document since. Note - // that we have to flush notifications, so that the entry will get updated - // properly. + // document" or know nothing about this ID yet but more content has been + // added to the document since. Note that we have to flush notifications, + // so that the entry will get updated properly. // Make sure to stash away the current generation so we can check whether // the table changes when we flush. @@ -2358,7 +2418,7 @@ nsHTMLDocument::GetElementById(const nsAString& aElementId, // We could now have a new entry, or the entry could have been // updated, so update e to point to the current entry's // mIdContent. - e = entry->mIdContent; + e = entry->GetIdContent(); } if (e == ID_NOT_IN_DOCUMENT) { @@ -2391,15 +2451,27 @@ nsHTMLDocument::GetElementById(const nsAString& aElementId, } if (!e) { +#ifdef DEBUG + // No reason to call MatchElementId if !IdTableIsLive, since + // we'd have done just that already + if (IdTableIsLive() && mRootContent && !aElementId.IsEmpty()) { + nsIContent* eDebug = + nsContentUtils::MatchElementId(mRootContent, aElementId); + NS_ASSERTION(!eDebug, + "We got null for |e| but MatchElementId found something?"); + } +#endif // There is no element with the given id in the document, cache // the fact that it's not in the document - entry->mIdContent = ID_NOT_IN_DOCUMENT; + entry->FlagIDNotInDocument(); return NS_OK; } // We found an element with a matching id, store that in the hash - entry->mIdContent = e; + if (NS_UNLIKELY(!entry->AddIdContent(e, PR_TRUE))) { + return NS_ERROR_OUT_OF_MEMORY; + } } return CallQueryInterface(e, aReturn); @@ -2888,7 +2960,7 @@ ReserveNameInHash(const nsAString& aName, PLDHashTable *aHash) NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); - entry->mContentList = NAME_NOT_VALID; + entry->mNameContentList = NAME_NOT_VALID; return NS_OK; } @@ -2975,7 +3047,7 @@ nsHTMLDocument::UpdateNameTableEntry(const nsAString& aName, return NS_OK; } - nsBaseContentList *list = entry->mContentList; + nsBaseContentList *list = entry->mNameContentList; if (!list || list == NAME_NOT_VALID) { return NS_OK; @@ -3001,13 +3073,11 @@ nsHTMLDocument::AddToIdTable(const nsAString& aId, nsIContent *aContent) PL_DHASH_ADD)); NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); - const nsIContent *e = entry->mIdContent; - - if (!e || e == ID_NOT_IN_DOCUMENT) { - entry->mIdContent = aContent; + if (NS_LIKELY(entry->AddIdContent(aContent, PR_FALSE))) { + return NS_OK; } - return NS_OK; + return NS_ERROR_OUT_OF_MEMORY; } nsresult @@ -3020,13 +3090,13 @@ nsHTMLDocument::UpdateIdTableEntry(const nsAString& aId, nsIContent *aContent) PL_DHashTableOperate(&mIdAndNameHashTable, &aId, op)); - if (!entry) { + NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); + + if (PL_DHASH_ENTRY_IS_BUSY(entry) && + NS_UNLIKELY(!entry->AddIdContent(aContent, PR_TRUE))) { + // failed to update... return NS_ERROR_OUT_OF_MEMORY; } - - if (PL_DHASH_ENTRY_IS_BUSY(entry)) { - entry->mIdContent = aContent; - } return NS_OK; } @@ -3043,9 +3113,9 @@ nsHTMLDocument::RemoveFromNameTable(const nsAString& aName, PL_DHashTableOperate(&mIdAndNameHashTable, &aName, PL_DHASH_LOOKUP)); - if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mContentList && - entry->mContentList != NAME_NOT_VALID) { - entry->mContentList->RemoveElement(aContent); + if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mNameContentList && + entry->mNameContentList != NAME_NOT_VALID) { + entry->mNameContentList->RemoveElement(aContent); } return NS_OK; @@ -3074,11 +3144,13 @@ nsHTMLDocument::RemoveFromIdTable(nsIContent *aContent) &value), PL_DHASH_LOOKUP)); - if (PL_DHASH_ENTRY_IS_FREE(entry) || entry->mIdContent != aContent) { + if (PL_DHASH_ENTRY_IS_FREE(entry)) { return NS_OK; } - PL_DHashTableRawRemove(&mIdAndNameHashTable, entry); + if (entry->RemoveIdContent(aContent)) { + PL_DHashTableRawRemove(&mIdAndNameHashTable, entry); + } return NS_OK; } @@ -3156,9 +3228,9 @@ static void FindNamedItems(const nsAString& aName, nsIContent *aContent, IdAndNameMapEntry& aEntry, PRBool aIsXHTML) { - NS_ASSERTION(aEntry.mContentList, + NS_ASSERTION(aEntry.mNameContentList, "Entry w/o content list passed to FindNamedItems()!"); - NS_ASSERTION(aEntry.mContentList != NAME_NOT_VALID, + NS_ASSERTION(aEntry.mNameContentList != NAME_NOT_VALID, "Entry that should never have a list passed to FindNamedItems()!"); if (aContent->IsContentOfType(nsIContent::eTEXT)) { @@ -3170,17 +3242,16 @@ FindNamedItems(const nsAString& aName, nsIContent *aContent, if (!aIsXHTML && IsNamedItem(aContent, aContent->Tag(), value) && value.Equals(aName)) { - aEntry.mContentList->AppendElement(aContent); + aEntry.mNameContentList->AppendElement(aContent); } - if (!aEntry.mIdContent) { + if (!aEntry.GetIdContent()) { + // Maybe this node has the right id? nsIAtom* idAttr = aContent->GetIDAttributeName(); - if (idAttr) { - aContent->GetAttr(kNameSpaceID_None, idAttr, value); - - if (value.Equals(aName)) { - aEntry.mIdContent = aContent; - } + if (idAttr && + aContent->AttrValueIs(kNameSpaceID_None, idAttr, + aName, eCaseMatters)) { + aEntry.AddIdContent(aContent, PR_FALSE); } } @@ -3213,13 +3284,13 @@ nsHTMLDocument::ResolveName(const nsAString& aName, PL_DHASH_ADD)); NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); - if (entry->mContentList == NAME_NOT_VALID) { + if (entry->mNameContentList == NAME_NOT_VALID) { // There won't be any named items by this name -- it's reserved return NS_OK; } // Now we know we _might_ have items. Before looking at - // entry->mContentList, make sure to flush out content (see + // entry->mNameContentList, make sure to flush out content (see // bug 69826). // This is a perf killer while the document is loading! @@ -3227,9 +3298,9 @@ nsHTMLDocument::ResolveName(const nsAString& aName, // table changes when we flush. PRUint32 generation = mIdAndNameHashTable.generation; - // If we already have an entry->mContentList, we need to flush out + // If we already have an entry->mNameContentList, we need to flush out // notifications too, so that it will get updated properly. - FlushPendingNotifications(entry->mContentList ? + FlushPendingNotifications(entry->mNameContentList ? Flush_ContentAndNotify : Flush_Content); if (generation != mIdAndNameHashTable.generation) { @@ -3244,7 +3315,7 @@ nsHTMLDocument::ResolveName(const nsAString& aName, } - nsBaseContentList *list = entry->mContentList; + nsBaseContentList *list = entry->mNameContentList; if (!list) { #ifdef DEBUG_jst @@ -3257,8 +3328,8 @@ nsHTMLDocument::ResolveName(const nsAString& aName, list = new nsBaseContentList(); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); - entry->mContentList = list; - NS_ADDREF(entry->mContentList); + entry->mNameContentList = list; + NS_ADDREF(entry->mNameContentList); if (mRootContent && !aName.IsEmpty()) { // We'll never get here if !IsXHTML(), so we can just pass @@ -3297,7 +3368,7 @@ nsHTMLDocument::ResolveName(const nsAString& aName, if (aForm) { // ... we're called from a form, in that case we create a - // nsFormContentList which will filter out the elements in the + // nsFormNameContentList which will filter out the elements in the // list that don't belong to aForm nsFormContentList *fc_list = new nsFormContentList(aForm, *list); @@ -3331,9 +3402,9 @@ nsHTMLDocument::ResolveName(const nsAString& aName, // No named items were found, see if there's one registerd by id for // aName. If we get this far, FindNamedItems() will have been called // for aName, so we're guaranteed that if there is an element with - // the id aName, it'll be in entry->mIdContent. + // the id aName, it'll be entry's IdContent. - nsIContent *e = entry->mIdContent; + nsIContent *e = entry->GetIdContent(); if (e && e != ID_NOT_IN_DOCUMENT && e->IsContentOfType(nsIContent::eHTML)) { nsIAtom *tag = e->Tag();