Fixing bug 118933. Speeding up the id and name hash tables in nsHTMLDocument by changing them over from nsHashtable/nsStirngKey to pldhash. Also combining the hashes to optimize performance and memory useage. r=heikki@netscape.com, sr=jband@netscape.com.

This commit is contained in:
jst%netscape.com 2002-01-25 06:37:35 +00:00
parent 808a02672c
commit b7db5ae3bd
7 changed files with 403 additions and 209 deletions

View File

@ -474,8 +474,6 @@ nsDocument::nsDocument() : mIsGoingAway(PR_FALSE),
nsCOMPtr<nsIDocumentObserver> observer(do_QueryInterface(mBindingManager));
if (observer) // We must always be the first observer of the document.
mObservers.InsertElementAt(observer, 0);
Init();/* XXX */
}
nsDocument::~nsDocument()

View File

@ -558,6 +558,8 @@ public:
NS_IMETHOD SetDocumentURL(nsIURI* aURI);
virtual nsresult Init();
protected:
NS_IMETHOD GetDTD(nsIDTD** aDTD) const;
@ -567,7 +569,6 @@ protected:
nsDocument();
virtual ~nsDocument();
nsresult Init();
nsIArena* mArena;
nsString mDocumentTitle;

View File

@ -761,10 +761,8 @@ HTMLContentSink::AddAttributes(const nsIParserNode& aNode,
ToLowerCase(k);
nsCOMPtr<nsIAtom> keyAtom(dont_AddRef(NS_NewAtom(k)));
nsHTMLValue value;
if (NS_CONTENT_ATTR_NOT_THERE ==
aContent->GetHTMLAttribute(keyAtom, value)) {
if (!aContent->HasAttr(kNameSpaceID_None, keyAtom)) {
// Get value and remove mandatory quotes
const nsAString& v = GetAttributeValueAt(aNode, i);

View File

@ -157,7 +157,7 @@ const PRInt32 kBackward = 1;
#include "nsHTMLContentSinkStream.h"
#endif
#define ELEMENT_NOT_IN_TABLE ((nsIContent *)1)
#define ID_NOT_IN_DOCUMENT ((nsIContent *)1)
static NS_DEFINE_CID(kCookieServiceCID, NS_COOKIESERVICE_CID);
@ -204,9 +204,85 @@ NS_EXPORT nsresult
NS_NewHTMLDocument(nsIDocument** aInstancePtrResult)
{
nsHTMLDocument* doc = new nsHTMLDocument();
if(doc)
return doc->QueryInterface(NS_GET_IID(nsIDocument), (void**) aInstancePtrResult);
return NS_ERROR_OUT_OF_MEMORY;
NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = doc->Init();
if (NS_FAILED(rv)) {
delete doc;
return rv;
}
*aInstancePtrResult = doc;
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
class IdAndNameMapEntry : public PLDHashEntryHdr
{
public:
IdAndNameMapEntry(const nsAString& aString) :
mKey(aString), mIdContent(nsnull), mContentList(nsnull)
{
}
~IdAndNameMapEntry()
{
NS_IF_RELEASE(mContentList);
}
nsString mKey;
nsIContent *mIdContent;
nsBaseContentList *mContentList;
};
PR_STATIC_CALLBACK(const void *)
IdAndNameHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
{
IdAndNameMapEntry *e = NS_STATIC_CAST(IdAndNameMapEntry *, entry);
return NS_STATIC_CAST(const nsAString *, &e->mKey);
}
PR_STATIC_CALLBACK(PLDHashNumber)
IdAndNameHashHashKey(PLDHashTable *table, const void *key)
{
const nsAString *str = NS_STATIC_CAST(const nsAString *, key);
return HashString(*str);
}
PR_STATIC_CALLBACK(PRBool)
IdAndNameHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
const void *key)
{
const IdAndNameMapEntry *e =
NS_STATIC_CAST(const IdAndNameMapEntry *, entry);
const nsAString *str = NS_STATIC_CAST(const nsAString *, key);
return str->Equals(e->mKey);
}
PR_STATIC_CALLBACK(void)
IdAndNameHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
{
IdAndNameMapEntry *e = NS_STATIC_CAST(IdAndNameMapEntry *, entry);
// An entry is being cleared, let the entry its own cleanup.
e->~IdAndNameMapEntry();
}
PR_STATIC_CALLBACK(void)
IdAndNameHashInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry,
const void *key)
{
const nsAString *keyStr = NS_STATIC_CAST(const nsAString *, key);
// Inititlize the entry with placement new
IdAndNameMapEntry *e = new (entry) IdAndNameMapEntry(*keyStr);
}
nsHTMLDocument::nsHTMLDocument()
@ -249,8 +325,6 @@ nsHTMLDocument::nsHTMLDocument()
}
mDomainWasSet = PR_FALSE; // Bug 13871: Frameset spoofing
PrePopulateHashTables();
}
nsHTMLDocument::~nsHTMLDocument()
@ -294,7 +368,9 @@ nsHTMLDocument::~nsHTMLDocument()
nsServiceManager::ReleaseService("@mozilla.org/rdf/rdf-service;1", gRDF);
}
InvalidateHashTables();
if (mIdAndNameHashIsLive) {
PL_DHashTableFinish(&mIdAndNameHashTable);
}
}
NS_IMPL_ADDREF_INHERITED(nsHTMLDocument, nsDocument)
@ -311,6 +387,35 @@ NS_INTERFACE_MAP_BEGIN(nsHTMLDocument)
NS_INTERFACE_MAP_END_INHERITING(nsDocument)
nsresult
nsHTMLDocument::Init()
{
nsresult rv = nsDocument::Init();
NS_ENSURE_SUCCESS(rv, rv);
static PLDHashTableOps hash_table_ops =
{
PL_DHashAllocTable,
PL_DHashFreeTable,
IdAndNameHashGetKey,
IdAndNameHashHashKey,
IdAndNameHashMatchEntry,
PL_DHashMoveEntryStub,
IdAndNameHashClearEntry,
PL_DHashFinalizeStub,
IdAndNameHashInitEntry
};
mIdAndNameHashIsLive = PL_DHashTableInit(&mIdAndNameHashTable,
&hash_table_ops, nsnull,
sizeof(IdAndNameMapEntry), 16);
NS_ENSURE_TRUE(mIdAndNameHashIsLive, NS_ERROR_OUT_OF_MEMORY);
PrePopulateHashTables();
return NS_OK;
}
NS_IMETHODIMP
nsHTMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
{
@ -1333,7 +1438,7 @@ nsHTMLDocument::AttributeChanged(nsIContent* aContent, PRInt32 aNameSpaceID,
aContent->GetTag(*getter_AddRefs(tag));
if (IsNamedItem(aContent, tag, value)) {
nsresult rv = AddToNameTable(value, aContent);
nsresult rv = UpdateNameTableEntry(value, aContent);
if (NS_FAILED(rv)) {
return rv;
@ -1345,7 +1450,7 @@ nsHTMLDocument::AttributeChanged(nsIContent* aContent, PRInt32 aNameSpaceID,
aContent->GetAttr(aNameSpaceID, nsHTMLAtoms::id, value);
if (!value.IsEmpty()) {
nsresult rv = AddToIdTable(value, aContent, PR_TRUE);
nsresult rv = AddToIdTable(value, aContent);
if (NS_FAILED(rv)) {
return rv;
@ -1375,23 +1480,18 @@ nsHTMLDocument::FlushPendingNotifications(PRBool aFlushReflows,
}
}
nsresult result = NS_OK;
if ((isSafeToFlush) && (mParser)) {
nsCOMPtr<nsIContentSink> sink;
// XXX Ack! Parser doesn't addref sink before passing it back
sink = mParser->GetContentSink();
if (sink) {
result = sink->FlushPendingNotifications();
nsresult rv = sink->FlushPendingNotifications();
NS_ENSURE_SUCCESS(rv, rv);
}
}
if (NS_SUCCEEDED(result)) {
result = nsDocument::FlushPendingNotifications(aFlushReflows,
aUpdateViews);
}
return result;
return nsDocument::FlushPendingNotifications(aFlushReflows, aUpdateViews);
}
NS_IMETHODIMP
@ -1446,7 +1546,8 @@ nsHTMLDocument::CreateElement(const nsAReadableString& aTagName,
*getter_AddRefs(nodeInfo));
nsCOMPtr<nsIHTMLContent> content;
nsresult rv = NS_CreateHTMLElement(getter_AddRefs(content), nodeInfo, PR_FALSE);
nsresult rv = NS_CreateHTMLElement(getter_AddRefs(content), nodeInfo,
PR_FALSE);
if (NS_SUCCEEDED(rv)) {
content->SetContentID(mNextContentID++);
rv = content->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aReturn);
@ -2361,35 +2462,43 @@ nsHTMLDocument::Close()
nsresult result = NS_OK;
if (mParser && mIsWriting) {
nsAutoString emptyStr(NS_LITERAL_STRING("</HTML>"));
mWriteLevel++;
result = mParser->Parse(emptyStr, NS_GENERATE_PARSER_KEY(),
result = mParser->Parse(NS_LITERAL_STRING("</HTML>"),
NS_GENERATE_PARSER_KEY(),
NS_LITERAL_STRING("text/html"), PR_FALSE,
PR_TRUE);
mWriteLevel--;
mIsWriting = 0;
NS_IF_RELEASE(mParser);
// XXX Make sure that all the document.written content is reflowed.
// We should remove this call once we change nsHTMLDocument::OpenCommon() so that it
// completely destroys the earlier document's content and frame hierarchy. Right now,
// it re-uses the earlier document's root content object and corresponding frame objects.
// These re-used frame objects think that they have already been reflowed, so they drop
// initial reflows. For certain cases of document.written content, like a frameset document,
// the dropping of the initial reflow means that we end up in document.close() without
// appended any reflow commands to the reflow queue and, consequently, without adding the
// dummy layout request to the load group. Since the dummy layout request is not added to
// the load group, the onload handler of the frameset fires before the frames get reflowed
// and loaded. That is the long explanation for why we need this one line of code here!
// XXX Make sure that all the document.written content is
// reflowed. We should remove this call once we change
// nsHTMLDocument::OpenCommon() so that it completely destroys the
// earlier document's content and frame hierarchy. Right now, it
// re-uses the earlier document's root content object and
// corresponding frame objects. These re-used frame objects think
// that they have already been reflowed, so they drop initial
// reflows. For certain cases of document.written content, like a
// frameset document, the dropping of the initial reflow means
// that we end up in document.close() without appended any reflow
// commands to the reflow queue and, consequently, without adding
// the dummy layout request to the load group. Since the dummy
// layout request is not added to the load group, the onload
// handler of the frameset fires before the frames get reflowed
// and loaded. That is the long explanation for why we need this
// one line of code here!
FlushPendingNotifications();
// Remove the doc write dummy request from the document load group
// that we added in OpenCommon(). If all other requests between
// document.open() and document.close() have completed, then this
// method should cause the firing of an onload event.
NS_ASSERTION(mDocWriteDummyRequest, "nsHTMLDocument::Close(): Trying to remove non-existent doc write dummy request!");
NS_ASSERTION(mDocWriteDummyRequest, "nsHTMLDocument::Close(): Trying to "
"remove non-existent doc write dummy request!");
RemoveDocWriteDummyRequest();
NS_ASSERTION(mDocWriteDummyRequest == nsnull, "nsHTMLDocument::Close(): Doc write dummy request could not be removed!");
NS_ASSERTION(mDocWriteDummyRequest == nsnull, "nsHTMLDocument::Close(): "
"Doc write dummy request could not be removed!");
}
return NS_OK;
@ -2586,37 +2695,39 @@ nsHTMLDocument::GetElementById(const nsAReadableString& aElementId,
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
if(!mRootContent) {
return NS_OK;
}
NS_WARN_IF_FALSE(!aElementId.IsEmpty(), "getElementById(\"\"), fix caller?");
if (aElementId.IsEmpty())
return NS_OK;
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(&mIdAndNameHashTable, &aElementId,
PL_DHASH_ADD));
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
nsStringKey key(aElementId);
nsIContent *e = entry->mIdContent;
nsIContent *e = NS_STATIC_CAST(nsIContent *, mIdHashTable.Get(&key));
if (e == ELEMENT_NOT_IN_TABLE) {
// We're looked for this id before and we didn't find it, so it's
// not in the document now either
if (e == ID_NOT_IN_DOCUMENT) {
// We've looked for this id before and we didn't find it, so it
// won't be in the document now either (since the
// mIdAndNameHashTable is live for entries in the table)
return NS_OK;
} else if (!e) {
e = MatchId(mRootContent, aElementId);
NS_WARN_IF_FALSE(!aElementId.IsEmpty(),
"getElementById(\"\") called, fix caller?");
if (mRootContent && !aElementId.IsEmpty()) {
e = MatchId(mRootContent, aElementId);
}
if (!e) {
// There is no element with the given id in the document, cache
// the fact that it's not in the document
mIdHashTable.Put(&key, ELEMENT_NOT_IN_TABLE);
entry->mIdContent = ID_NOT_IN_DOCUMENT;
return NS_OK;
}
// We found an element with a matching id, store that in the hash
mIdHashTable.Put(&key, e);
entry->mIdContent = e;
}
return CallQueryInterface(e, aReturn);
@ -2706,10 +2817,8 @@ nsHTMLDocument::GetPixelDimensions(nsIPresShell* aShell,
{
*aWidth = *aHeight = 0;
nsresult result;
result = FlushPendingNotifications();
if (NS_FAILED(result))
return NS_OK;
nsresult result = FlushPendingNotifications();
NS_ENSURE_SUCCESS(result, result);
// Find the <body> element: this is what we'll want to use for the
// document's width and height values.
@ -3168,26 +3277,40 @@ NameHashCleanupEnumeratorCallback(nsHashKey *aKey, void *aData, void* closure)
return PR_TRUE;
}
PR_STATIC_CALLBACK(PLDHashOperator)
IdAndNameMapEntryRemoveCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
PRUint32 number, void *arg)
{
return PL_DHASH_REMOVE;
}
void
nsHTMLDocument::InvalidateHashTables()
{
mNameHashTable.Reset(NameHashCleanupEnumeratorCallback);
mIdHashTable.Reset();
PL_DHashTableEnumerate(&mIdAndNameHashTable, IdAndNameMapEntryRemoveCallback,
nsnull);
}
static nsresult
AddEmptyListToHash(const nsAReadableString& aName, nsHashtable& aHash)
AddEmptyListToHash(const nsAString& aName, PLDHashTable *aHash)
{
nsBaseContentList *list = new nsBaseContentList();
if (!list) {
return NS_ERROR_OUT_OF_MEMORY;
}
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(list);
nsStringKey key(aName);
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(aHash, &aName, PL_DHASH_ADD));
aHash.Put(&key, list);
if (!entry) {
NS_RELEASE(list);
return NS_ERROR_OUT_OF_MEMORY;
}
entry->mContentList = list;
return NS_OK;
}
@ -3201,34 +3324,36 @@ nsHTMLDocument::PrePopulateHashTables()
{
nsresult rv = NS_OK;
rv = AddEmptyListToHash(NS_LITERAL_STRING("write"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("write"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("writeln"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("writeln"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("open"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("open"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("close"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("close"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("forms"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("forms"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("elements"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("elements"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("characterSet"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("characterSet"),
&mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("nodeType"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("nodeType"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("parentNode"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("parentNode"),
&mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddEmptyListToHash(NS_LITERAL_STRING("cookie"), mNameHashTable);
rv = AddEmptyListToHash(NS_LITERAL_STRING("cookie"), &mIdAndNameHashTable);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
@ -3241,9 +3366,11 @@ IsNamedItem(nsIContent* aContent, nsIAtom *aTag, nsAWritableString& aName)
// attribute are registered. Images, layers and forms always get
// reflected up to the document. Applets and embeds only go
// to the closest container (which could be a form).
if ((aTag == nsHTMLAtoms::img) || (aTag == nsHTMLAtoms::form) ||
(aTag == nsHTMLAtoms::applet) || (aTag == nsHTMLAtoms::embed) ||
(aTag == nsHTMLAtoms::object)) {
if (aTag == nsHTMLAtoms::img ||
aTag == nsHTMLAtoms::form ||
aTag == nsHTMLAtoms::applet ||
aTag == nsHTMLAtoms::embed ||
aTag == nsHTMLAtoms::object) {
aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::name, aName);
if (!aName.IsEmpty()) {
@ -3255,13 +3382,19 @@ IsNamedItem(nsIContent* aContent, nsIAtom *aTag, nsAWritableString& aName)
}
nsresult
nsHTMLDocument::AddToNameTable(const nsAReadableString& aName,
nsIContent *aContent)
nsHTMLDocument::UpdateNameTableEntry(const nsAString& aName,
nsIContent *aContent)
{
nsStringKey key(aName);
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(&mIdAndNameHashTable, &aName,
PL_DHASH_LOOKUP));
nsBaseContentList* list = NS_STATIC_CAST(nsBaseContentList *,
mNameHashTable.Get(&key));
if (!PL_DHASH_ENTRY_IS_LIVE(entry)) {
return NS_OK;
}
nsBaseContentList *list = entry->mContentList;
if (!list) {
return NS_OK;
@ -3279,31 +3412,49 @@ nsHTMLDocument::AddToNameTable(const nsAReadableString& aName,
}
nsresult
nsHTMLDocument::AddToIdTable(const nsAReadableString& aId,
nsIContent *aContent, PRBool aPutInTable)
nsHTMLDocument::AddToIdTable(const nsAString& aId, nsIContent *aContent)
{
nsStringKey key(aId);
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(&mIdAndNameHashTable, &aId,
PL_DHASH_ADD));
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
nsIContent *e = NS_STATIC_CAST(nsIContent *, mIdHashTable.Get(&key));
const nsIContent *e = entry->mIdContent;
if (e == ELEMENT_NOT_IN_TABLE || (!e && aPutInTable)) {
mIdHashTable.Put(&key, aContent);
if (!e || e == ID_NOT_IN_DOCUMENT) {
entry->mIdContent = aContent;
}
return NS_OK;
}
nsresult
nsHTMLDocument::RemoveFromNameTable(const nsAReadableString& aName,
nsHTMLDocument::UpdateIdTableEntry(const nsAString& aId, nsIContent *aContent)
{
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(&mIdAndNameHashTable, &aId,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_LIVE(entry)) {
entry->mIdContent = aContent;
}
return NS_OK;
}
nsresult
nsHTMLDocument::RemoveFromNameTable(const nsAString& aName,
nsIContent *aContent)
{
nsStringKey key(aName);
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(&mIdAndNameHashTable, &aName,
PL_DHASH_LOOKUP));
nsBaseContentList* list = NS_STATIC_CAST(nsBaseContentList *,
mNameHashTable.Get(&key));
if (list) {
list->RemoveElement(aContent);
if (PL_DHASH_ENTRY_IS_LIVE(entry) && entry->mContentList) {
entry->mContentList->RemoveElement(aContent);
}
return NS_OK;
@ -3312,22 +3463,29 @@ nsHTMLDocument::RemoveFromNameTable(const nsAReadableString& aName,
nsresult
nsHTMLDocument::RemoveFromIdTable(nsIContent *aContent)
{
nsAutoString value;
if (!aContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::id)) {
return NS_OK;
}
nsAutoString value;
aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, value);
if (value.IsEmpty()) {
return NS_OK;
}
nsStringKey key(value);
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(&mIdAndNameHashTable,
NS_STATIC_CAST(const nsAString *,
&value),
PL_DHASH_LOOKUP));
nsIContent *e = NS_STATIC_CAST(nsIContent *, mIdHashTable.Get(&key));
if (e != aContent)
if (!PL_DHASH_ENTRY_IS_LIVE(entry) || entry->mIdContent != aContent) {
return NS_OK;
}
mIdHashTable.Remove(&key);
PL_DHashTableRawRemove(&mIdAndNameHashTable, entry);
return NS_OK;
}
@ -3395,13 +3553,13 @@ nsHTMLDocument::RegisterNamedItems(nsIContent *aContent)
nsAutoString value;
if (IsNamedItem(aContent, tag, value)) {
AddToNameTable(value, aContent);
UpdateNameTableEntry(value, aContent);
}
aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, value);
if (!value.IsEmpty()) {
nsresult rv = AddToIdTable(value, aContent, PR_FALSE);
nsresult rv = UpdateIdTableEntry(value, aContent);
if (NS_FAILED(rv)) {
return rv;
@ -3425,13 +3583,14 @@ nsHTMLDocument::RegisterNamedItems(nsIContent *aContent)
return NS_OK;
}
void
nsHTMLDocument::FindNamedItems(const nsAReadableString& aName,
nsIContent *aContent, nsBaseContentList& aList)
static void
FindNamedItems(const nsAReadableString& aName, nsIContent *aContent,
IdAndNameMapEntry& aEntry)
{
nsCOMPtr<nsIAtom> tag;
nsAutoString value;
NS_ASSERTION(aEntry.mContentList,
"Entry w/o content list passed to FindNamedItems()!");
nsCOMPtr<nsIAtom> tag;
aContent->GetTag(*getter_AddRefs(tag));
if (tag == nsLayoutAtoms::textTagName) {
@ -3440,13 +3599,17 @@ nsHTMLDocument::FindNamedItems(const nsAReadableString& aName,
return;
}
nsAutoString value;
if (IsNamedItem(aContent, tag, value) && value.Equals(aName)) {
aList.AppendElement(aContent);
} else {
aEntry.mContentList->AppendElement(aContent);
}
if (!aEntry.mIdContent) {
aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, value);
if (value.Equals(aName)) {
AddToIdTable(value, aContent, PR_TRUE);
aEntry.mIdContent = aContent;
}
}
@ -3459,7 +3622,7 @@ nsHTMLDocument::FindNamedItems(const nsAReadableString& aName,
for (i = 0; i < count; i++) {
aContent->ChildAt(i, *getter_AddRefs(child));
FindNamedItems(aName, child, aList);
FindNamedItems(aName, child, aEntry);
}
}
@ -3476,13 +3639,16 @@ nsHTMLDocument::ResolveName(const nsAReadableString& aName,
// This is a perf killer while the document is loading!
FlushPendingNotifications(PR_FALSE);
nsStringKey key(aName);
// We have built a table and cache the named items. The table will
// be updated as content is added and removed.
nsBaseContentList *list = NS_STATIC_CAST(nsContentList *,
mNameHashTable.Get(&key));
IdAndNameMapEntry *entry =
NS_STATIC_CAST(IdAndNameMapEntry *,
PL_DHashTableOperate(&mIdAndNameHashTable, &aName,
PL_DHASH_ADD));
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
nsBaseContentList *list = entry->mContentList;
if (!list) {
#ifdef DEBUG_jst
@ -3495,100 +3661,102 @@ nsHTMLDocument::ResolveName(const nsAReadableString& aName,
list = new nsBaseContentList();
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(list);
entry->mContentList = list;
NS_ADDREF(entry->mContentList);
if(mRootContent) {
FindNamedItems(aName, mRootContent, *list);
if(mRootContent && !aName.IsEmpty()) {
FindNamedItems(aName, mRootContent, *entry);
}
mNameHashTable.Put(&key, list);
}
PRUint32 length;
list->GetLength(&length);
if (length == 1) {
// Onle one element in the list, return the list in stead of
// returning the list
if (length) {
if (length == 1) {
// Onle one element in the list, return the list in stead of
// returning the list
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsIDOMNode> node;
list->Item(0, getter_AddRefs(node));
list->Item(0, getter_AddRefs(node));
if (aForm && node) {
// document.forms["foo"].bar should not map to <form name="bar">
// so we check here to see if we found a form and if we did we
// ignore what we found in the document. This doesn't deal with
// the case where more than one element in found in the document
// (i.e. there are two named items in the document that have the
// name we're looking for), that case is dealt with in
// nsFormContentList
if (aForm && node) {
// document.forms["foo"].bar should not map to <form
// name="bar"> so we check here to see if we found a form and
// if we did we ignore what we found in the document. This
// doesn't deal with the case where more than one element in
// found in the document (i.e. there are two named items in
// the document that have the name we're looking for), that
// case is dealt with in nsFormContentList
nsCOMPtr<nsIDOMHTMLFormElement> f(do_QueryInterface(node));
nsCOMPtr<nsIDOMHTMLFormElement> f(do_QueryInterface(node));
if (f) {
node = nsnull;
}
}
*aResult = node;
NS_IF_ADDREF(*aResult);
return NS_OK;
}
if (length > 1) {
// The list contains more than one element, return the whole list,
// unless...
if (aForm) {
// ... we're called from a form, in that case we create a
// nsFormContentList which will filter out the elements in the
// list that don't belong to aForm
nsFormContentList *fc_list = new nsFormContentList(aForm, *list);
NS_ENSURE_TRUE(fc_list, NS_ERROR_OUT_OF_MEMORY);
PRUint32 len;
fc_list->GetLength(&len);
if (len < 2) {
// After t nsFormContentList is done filtering there's zero or
// one element in the list, return that element, or null if
// there's no element in the list.
nsCOMPtr<nsIDOMNode> node;
fc_list->Item(0, getter_AddRefs(node));
*aResult = node;
NS_IF_ADDREF(*aResult);
delete fc_list;
return NS_OK;
if (f) {
node = nsnull;
}
}
list = fc_list;
*aResult = node;
NS_IF_ADDREF(*aResult);
return NS_OK;
}
return list->QueryInterface(NS_GET_IID(nsISupports), (void **)aResult);
if (length > 1) {
// The list contains more than one element, return the whole
// list, unless...
if (aForm) {
// ... we're called from a form, in that case we create a
// nsFormContentList which will filter out the elements in the
// list that don't belong to aForm
nsFormContentList *fc_list = new nsFormContentList(aForm, *list);
NS_ENSURE_TRUE(fc_list, NS_ERROR_OUT_OF_MEMORY);
PRUint32 len;
fc_list->GetLength(&len);
if (len < 2) {
// After t nsFormContentList is done filtering there's zero
// or one element in the list, return that element, or null
// if there's no element in the list.
nsCOMPtr<nsIDOMNode> node;
fc_list->Item(0, getter_AddRefs(node));
*aResult = node;
NS_IF_ADDREF(*aResult);
delete fc_list;
return NS_OK;
}
list = fc_list;
}
return list->QueryInterface(NS_GET_IID(nsISupports), (void **)aResult);
}
}
// No named items were found, look if we'll find the name by id.
// 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.
nsIContent *e = NS_STATIC_CAST(nsIContent *, mIdHashTable.Get(&key));
nsIContent *e = entry->mIdContent;
if (e && e != ELEMENT_NOT_IN_TABLE) {
if (e && e != ID_NOT_IN_DOCUMENT) {
nsCOMPtr<nsIAtom> tag;
e->GetTag(*getter_AddRefs(tag));
if (tag.get() == nsHTMLAtoms::embed ||
tag.get() == nsHTMLAtoms::img ||
tag.get() == nsHTMLAtoms::object ||
tag.get() == nsHTMLAtoms::applet) {
if (tag == nsHTMLAtoms::embed ||
tag == nsHTMLAtoms::img ||
tag == nsHTMLAtoms::object ||
tag == nsHTMLAtoms::applet) {
*aResult = e;
NS_ADDREF(*aResult);
}

View File

@ -45,11 +45,11 @@
#include "nsIDOMNSHTMLDocument.h"
#include "nsIDOMHTMLBodyElement.h"
#include "nsIHTMLContentContainer.h"
#include "nsHashtable.h"
#include "jsapi.h"
#include "rdf.h"
#include "nsRDFCID.h"
#include "nsIRDFService.h"
#include "pldhash.h"
// Doc write dummy request
#include "nsIChannel.h"
@ -78,6 +78,7 @@ class nsHTMLDocument : public nsMarkupDocument,
public:
nsHTMLDocument();
virtual ~nsHTMLDocument();
virtual nsresult Init();
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
@ -182,20 +183,17 @@ protected:
nsresult RegisterNamedItems(nsIContent *aContent);
nsresult UnregisterNamedItems(nsIContent *aContent);
nsresult AddToNameTable(const nsAReadableString& aName,
nsIContent *aContent);
nsresult AddToIdTable(const nsAReadableString& aId, nsIContent *aContent,
PRBool aPutInTable);
nsresult RemoveFromNameTable(const nsAReadableString& aName,
nsIContent *aContent);
nsresult UpdateNameTableEntry(const nsAString& aName,
nsIContent *aContent);
nsresult AddToIdTable(const nsAString& aId, nsIContent *aContent);
nsresult UpdateIdTableEntry(const nsAString& aId, nsIContent *aContent);
nsresult RemoveFromNameTable(const nsAString& aName, nsIContent *aContent);
nsresult RemoveFromIdTable(nsIContent *aContent);
void InvalidateHashTables();
nsresult PrePopulateHashTables();
nsIContent *MatchId(nsIContent *aContent, const nsAReadableString& aId);
void FindNamedItems(const nsAReadableString& aName, nsIContent *aContent,
nsBaseContentList& aList);
virtual void InternalAddStyleSheet(nsIStyleSheet* aSheet);
virtual void InternalInsertStyleSheetAt(nsIStyleSheet* aSheet,
@ -287,10 +285,11 @@ protected:
/*
* Bug 13871: Frameset spoofing - find out if document.domain was set
*/
PRBool mDomainWasSet;
PRPackedBool mDomainWasSet;
PRPackedBool mIdAndNameHashIsLive;
PLDHashTable mIdAndNameHashTable;
nsHashtable mNameHashTable;
nsHashtable mIdHashTable;
nsCOMPtr<nsIRequest> mDocWriteDummyRequest;
};

View File

@ -224,9 +224,20 @@ NS_EXPORT nsresult
NS_NewImageDocument(nsIDocument** aInstancePtrResult)
{
nsImageDocument* doc = new nsImageDocument();
if(doc)
return doc->QueryInterface(NS_GET_IID(nsIDocument), (void**) aInstancePtrResult);
return NS_ERROR_OUT_OF_MEMORY;
NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = doc->Init();
if (NS_FAILED(rv)) {
delete doc;
return rv;
}
*aInstancePtrResult = doc;
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
nsImageDocument::nsImageDocument()

View File

@ -153,9 +153,17 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
*aInstancePtrResult = nsnull;
nsXMLDocument* doc = new nsXMLDocument();
if (doc == nsnull)
if (!doc)
return NS_ERROR_OUT_OF_MEMORY;
rv = doc->Init();
if (NS_FAILED(rv)) {
delete doc;
return rv;
}
nsCOMPtr<nsIDOMDocument> kungFuDeathGrip(doc);
rv = doc->Reset(nsnull, nsnull);
@ -193,9 +201,20 @@ NS_EXPORT nsresult
NS_NewXMLDocument(nsIDocument** aInstancePtrResult)
{
nsXMLDocument* doc = new nsXMLDocument();
if (doc == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
return doc->QueryInterface(NS_GET_IID(nsIDocument), (void**) aInstancePtrResult);
NS_ENSURE_TRUE(doc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = doc->Init();
if (NS_FAILED(rv)) {
delete doc;
return rv;
}
*aInstancePtrResult = doc;
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
nsXMLDocument::nsXMLDocument()