From 44345760e7c8d50ff64334434e4cd978da0befb6 Mon Sep 17 00:00:00 2001 From: "jst%netscape.com" Date: Thu, 22 Mar 2001 08:51:52 +0000 Subject: [PATCH] Fixing bugs 64523, 50018, 57626, 59024, 61413 and probably others, making the document and form JS engine resolve hooks find what they're supposed to find, and nothing more, making the element-by-name and element-by-id lookup in the document be hashtable based to avoid walking the whole DOM tree over and over again when resolving names on the document object and also on form objects. This is an order of magnitude speedup for pages that contain a large number of form controls, such as hotmail and aol mail. Also did a bunch of cleanup here n' there. r=pollmann@netscape.com, sr=vidur@netscape.comI. git-svn-id: svn://10.0.0.236/trunk@90103 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/content/base/public/nsIDocument.h | 3 + mozilla/content/base/src/nsContentList.cpp | 485 ++++++-- mozilla/content/base/src/nsContentList.h | 75 +- mozilla/content/base/src/nsDocument.cpp | 11 +- mozilla/content/base/src/nsDocument.h | 7 +- .../content/base/src/nsGenericDOMDataNode.cpp | 1 - .../content/base/src/nsGenericDOMDataNode.h | 1 - mozilla/content/base/src/nsGenericElement.cpp | 18 +- mozilla/content/base/src/nsGenericElement.h | 1 - .../html/content/public/nsIFormControl.h | 3 +- .../html/content/src/nsGenericHTMLElement.cpp | 186 ++- .../html/content/src/nsGenericHTMLElement.h | 11 +- .../html/content/src/nsHTMLFormElement.cpp | 406 +++--- .../html/content/src/nsHTMLUnknownElement.cpp | 2 + .../html/document/src/nsHTMLDocument.cpp | 1096 +++++++++++------ .../html/document/src/nsHTMLDocument.h | 42 +- .../html/document/src/nsIHTMLDocument.h | 5 + .../content/xul/content/src/nsXULElement.cpp | 7 +- .../xul/document/src/nsXULDocument.cpp | 8 + .../content/xul/document/src/nsXULDocument.h | 3 + 20 files changed, 1576 insertions(+), 795 deletions(-) diff --git a/mozilla/content/base/public/nsIDocument.h b/mozilla/content/base/public/nsIDocument.h index ca785be205f..2aacf200dce 100644 --- a/mozilla/content/base/public/nsIDocument.h +++ b/mozilla/content/base/public/nsIDocument.h @@ -256,6 +256,9 @@ public: // either may be nsnull, but not both NS_IMETHOD ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2) = 0; + NS_IMETHOD AttributeWillChange(nsIContent* aChild, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute) = 0; NS_IMETHOD AttributeChanged(nsIContent* aChild, PRInt32 aNameSpaceID, nsIAtom* aAttribute, diff --git a/mozilla/content/base/src/nsContentList.cpp b/mozilla/content/base/src/nsContentList.cpp index cb08c0c7e59..a028e02973d 100644 --- a/mozilla/content/base/src/nsContentList.cpp +++ b/mozilla/content/base/src/nsContentList.cpp @@ -32,10 +32,287 @@ #include "nsLayoutAtoms.h" #include "nsHTMLAtoms.h" // XXX until atoms get factored into nsLayoutAtoms -nsContentList::nsContentList(nsIDocument *aDocument) +// Form related includes +#include "nsIDOMHTMLFormElement.h" + +nsBaseContentList::nsBaseContentList() + : mScriptObject(nsnull) { NS_INIT_REFCNT(); - mScriptObject = nsnull; +} + +nsBaseContentList::~nsBaseContentList() +{ + // mElements only has weak references to the content objects so we + // don't need to do any cleanup here. +} + + +NS_IMPL_ADDREF(nsBaseContentList) +NS_IMPL_RELEASE(nsBaseContentList) + +NS_INTERFACE_MAP_BEGIN(nsBaseContentList) + NS_INTERFACE_MAP_ENTRY(nsIDOMNodeList) + NS_INTERFACE_MAP_ENTRY(nsIScriptObjectOwner) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeList) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +nsBaseContentList::GetLength(PRUint32* aLength) +{ + *aLength = mElements.Count(); + + return NS_OK; +} + +NS_IMETHODIMP +nsBaseContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn) +{ + nsISupports *tmp = NS_REINTERPRET_CAST(nsISupports *, + mElements.ElementAt(aIndex)); + + if (!tmp) { + *aReturn = nsnull; + + return NS_OK; + } + + return CallQueryInterface(tmp, aReturn); +} + +NS_IMETHODIMP +nsBaseContentList::GetScriptObject(nsIScriptContext *aContext, + void** aScriptObject) +{ + nsresult res = NS_OK; + nsIScriptGlobalObject *global = aContext->GetGlobalObject(); + + if (!mScriptObject) { + nsIDOMScriptObjectFactory *factory; + + res = nsGenericElement::GetScriptObjectFactory(&factory); + if (NS_FAILED(res)) { + return res; + } + + res = factory->NewScriptNodeList(aContext, + NS_STATIC_CAST(nsIDOMNodeList *, this), + global, &mScriptObject); + NS_RELEASE(factory); + } + + *aScriptObject = mScriptObject; + + NS_RELEASE(global); + + return res; +} + +NS_IMETHODIMP +nsBaseContentList::SetScriptObject(void *aScriptObject) +{ + mScriptObject = aScriptObject; + return NS_OK; +} + +NS_IMETHODIMP +nsBaseContentList::AppendElement(nsIContent *aContent) +{ + // Shouldn't hold a reference since we'll be told when the content + // leaves the document or the document will be destroyed. + mElements.AppendElement(aContent); + + return NS_OK; +} + +NS_IMETHODIMP +nsBaseContentList::RemoveElement(nsIContent *aContent) +{ + mElements.RemoveElement(aContent); + + return NS_OK; +} + +NS_IMETHODIMP +nsBaseContentList::IndexOf(nsIContent *aContent, PRInt32& aIndex) +{ + aIndex = mElements.IndexOf(aContent); + + return NS_OK; +} + +NS_IMETHODIMP +nsBaseContentList::Reset() +{ + mElements.Clear(); + + return NS_OK; +} + +// nsFormContentList + +// This helper function checks if aContent is in some way associated +// with aForm, this check is only successful if the form is a +// container (and a form is a container as long as the document is +// wellformed). If the form is a container the only elements that are +// considerd to be associated with a form are the elements that are +// contained within the form. If the form is a leaf element then all +// the elements will be accepted into this list. + +static PRBool BelongsInForm(nsIDOMHTMLFormElement *aForm, + nsIContent *aContent) +{ + nsCOMPtr form(do_QueryInterface(aForm)); + + if (!form) { + NS_WARNING("This should not happen, form is not an nsIContent!"); + + return PR_TRUE; + } + + if (form.get() == aContent) { + // The list for aForm contains the form itself, forms should not + // be reachable by name in the form namespace, so we return false + // here. + + return PR_FALSE; + } + + nsCOMPtr content; + + aContent->GetParent(*getter_AddRefs(content)); + + while (content) { + if (content == form) { + // aContent is contained within the form so we return true. + + return PR_TRUE; + } + + nsCOMPtr tag; + + content->GetTag(*getter_AddRefs(tag)); + + if (tag.get() == nsHTMLAtoms::form) { + // The child is contained within a form, but not the right form + // so we ignore it. + + return PR_FALSE; + } + + nsIContent *tmp = content; + + tmp->GetParent(*getter_AddRefs(content)); + } + + PRInt32 count = 0; + + form->ChildCount(count); + + if (!count) { + // The form is a leaf and aContent wasn't inside any other form so + // we return true + + return PR_TRUE; + } + + // The form is a container but aContent wasn't inside the form, + // return false + + return PR_FALSE; +} + +nsFormContentList::nsFormContentList(nsIDOMHTMLFormElement *aForm, + nsBaseContentList& aContentList) + : nsBaseContentList() +{ + NS_INIT_REFCNT(); + + // move elements that belong to mForm into this content list + + PRUint32 i, length = 0; + nsCOMPtr item; + + aContentList.GetLength(&length); + + for (i = 0; i < length; i++) { + aContentList.Item(i, getter_AddRefs(item)); + + nsCOMPtr c(do_QueryInterface(item)); + + if (c && BelongsInForm(aForm, c)) { + AppendElement(c); + } + } +} + +nsFormContentList::~nsFormContentList() +{ + Reset(); +} + +NS_IMETHODIMP +nsFormContentList::AppendElement(nsIContent *aContent) +{ + NS_ADDREF(aContent); + + return nsBaseContentList::AppendElement(aContent); +} + +NS_IMETHODIMP +nsFormContentList::RemoveElement(nsIContent *aContent) +{ + PRInt32 i = mElements.IndexOf(aContent); + + if (i >= 0) { + nsIContent *content = NS_STATIC_CAST(nsIContent *, mElements.ElementAt(i)); + + NS_RELEASE(content); + + mElements.RemoveElementAt(i); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsFormContentList::Reset() +{ + PRInt32 i, length = mElements.Count(); + + for (i = 0; i < length; i++) { + nsIContent *content = NS_STATIC_CAST(nsIContent *, mElements.ElementAt(i)); + + NS_RELEASE(content); + } + + return nsBaseContentList::Reset(); +} + + +// nsContentList implementation + +nsContentList::nsContentList(const nsContentList& aContentList) + : nsBaseContentList() +{ + mFunc = aContentList.mFunc; + mMatchAtom = aContentList.mMatchAtom; + mDocument = aContentList.mDocument; + + if (aContentList.mData) { + mData = new nsString(*aContentList.mData); + } else { + mData = nsnull; + } + + mMatchAll = aContentList.mMatchAll; + mRootContent = aContentList.mRootContent; + mElements = aContentList.mElements; +} + +nsContentList::nsContentList(nsIDocument *aDocument) + : nsBaseContentList() +{ mFunc = nsnull; mMatchAtom = nsnull; mDocument = aDocument; @@ -48,6 +325,7 @@ nsContentList::nsContentList(nsIDocument *aDocument, nsIAtom* aMatchAtom, PRInt32 aMatchNameSpaceId, nsIContent* aRootContent) + : nsBaseContentList() { mMatchAtom = aMatchAtom; NS_IF_ADDREF(mMatchAtom); @@ -64,14 +342,15 @@ nsContentList::nsContentList(nsIDocument *aDocument, Init(aDocument); } -nsContentList::nsContentList(nsIDocument *aDocument, +nsContentList::nsContentList(nsIDocument *aDocument, nsContentListMatchFunc aFunc, - const nsString* aData, + const nsAReadableString& aData, nsIContent* aRootContent) + : nsBaseContentList() { mFunc = aFunc; - if (nsnull != aData) { - mData = new nsString(*aData); + if (!aData.IsEmpty()) { + mData = new nsString(aData); // If this fails, fail silently } else { @@ -85,70 +364,46 @@ nsContentList::nsContentList(nsIDocument *aDocument, void nsContentList::Init(nsIDocument *aDocument) { - NS_INIT_REFCNT(); - mScriptObject = nsnull; // We don't reference count the reference to the document // If the document goes away first, we'll be informed and we // can drop our reference. // If we go away first, we'll get rid of ourselves from the // document's observer list. mDocument = aDocument; - if (nsnull != mDocument) { + if (mDocument) { mDocument->AddObserver(this); } PopulateSelf(); } - + nsContentList::~nsContentList() { - if (nsnull != mDocument) { + if (mDocument) { mDocument->RemoveObserver(this); } NS_IF_RELEASE(mMatchAtom); - if (nsnull != mData) { - delete mData; - } + delete mData; } -nsresult nsContentList::QueryInterface(REFNSIID aIID, void** aInstancePtr) -{ - if (nsnull == aInstancePtr) { - return NS_ERROR_NULL_POINTER; - } - if (aIID.Equals(NS_GET_IID(nsIDOMNodeList))) { - *aInstancePtr = (void*)(nsIDOMNodeList*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsIDOMHTMLCollection))) { - *aInstancePtr = (void*)(nsIDOMHTMLCollection*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsIScriptObjectOwner))) { - *aInstancePtr = (void*)(nsIScriptObjectOwner*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsISupports))) { - *aInstancePtr = (void*)(nsISupports*)(nsIDOMNodeList*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - return NS_NOINTERFACE; -} +NS_INTERFACE_MAP_BEGIN(nsContentList) + NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLCollection) +NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList) -NS_IMPL_ADDREF(nsContentList) -NS_IMPL_RELEASE(nsContentList) +NS_IMPL_ADDREF_INHERITED(nsContentList, nsBaseContentList) +NS_IMPL_RELEASE_INHERITED(nsContentList, nsBaseContentList) NS_IMETHODIMP nsContentList::GetLength(PRUint32* aLength) { nsresult result = CheckDocumentExistence(); - if (NS_OK == result) { - *aLength = mContent.Count(); + if (NS_SUCCEEDED(result)) { + if (mDocument) { + mDocument->FlushPendingNotifications(PR_FALSE); + } + + *aLength = mElements.Count(); } return result; @@ -158,15 +413,17 @@ NS_IMETHODIMP nsContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn) { nsresult result = CheckDocumentExistence(); - if (NS_OK == result) { - if (nsnull != mDocument) { - mDocument->FlushPendingNotifications(PR_FALSE); // Flush pending content changes Bug 4891 + if (NS_SUCCEEDED(result)) { + if (mDocument) { + // Flush pending content changes Bug 4891 + mDocument->FlushPendingNotifications(PR_FALSE); } - nsISupports *element = (nsISupports *)mContent.ElementAt(aIndex); + nsISupports *element = NS_STATIC_CAST(nsISupports *, + mElements.ElementAt(aIndex)); - if (nsnull != element) { - result = element->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aReturn); + if (element) { + result = CallQueryInterface(element, aReturn); } else { *aReturn = nsnull; @@ -181,23 +438,24 @@ nsContentList::NamedItem(const nsAReadableString& aName, nsIDOMNode** aReturn) { nsresult result = CheckDocumentExistence(); - if (NS_OK == result) { - if (nsnull != mDocument) { + if (NS_SUCCEEDED(result)) { + if (mDocument) { mDocument->FlushPendingNotifications(PR_FALSE); // Flush pending content changes Bug 4891 } - PRInt32 i, count = mContent.Count(); + PRInt32 i, count = mElements.Count(); for (i = 0; i < count; i++) { - nsIContent *content = (nsIContent *)mContent.ElementAt(i); - if (nsnull != content) { + nsIContent *content = NS_STATIC_CAST(nsIContent *, + mElements.ElementAt(i)); + if (content) { nsAutoString name; // XXX Should it be an EqualsIgnoreCase? if (((content->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::name, name) == NS_CONTENT_ATTR_HAS_VALUE) && (aName.Equals(name))) || ((content->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::id, name) == NS_CONTENT_ATTR_HAS_VALUE) && (aName.Equals(name)))) { - return content->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aReturn); + return CallQueryInterface(content, aReturn); } } } @@ -208,55 +466,51 @@ nsContentList::NamedItem(const nsAReadableString& aName, nsIDOMNode** aReturn) } NS_IMETHODIMP -nsContentList::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject) +nsContentList::GetScriptObject(nsIScriptContext *aContext, + void** aScriptObject) { nsresult res = NS_OK; nsIScriptGlobalObject *global = aContext->GetGlobalObject(); - if (nsnull == mScriptObject) { + if (!mScriptObject) { nsIDOMScriptObjectFactory *factory; - + res = nsGenericElement::GetScriptObjectFactory(&factory); - if (NS_OK != res) { + if (NS_FAILED(res)) { return res; } - res = factory->NewScriptHTMLCollection(aContext, - (nsISupports*)(nsIDOMHTMLCollection*)this, - global, - (void**)&mScriptObject); + res = factory->NewScriptHTMLCollection(aContext, + NS_STATIC_CAST(nsIDOMNodeList *, + this), + global, &mScriptObject); NS_RELEASE(factory); } + *aScriptObject = mScriptObject; NS_RELEASE(global); + return res; } -NS_IMETHODIMP -nsContentList::SetScriptObject(void *aScriptObject) -{ - mScriptObject = aScriptObject; - return NS_OK; -} - - NS_IMETHODIMP -nsContentList::ContentAppended(nsIDocument *aDocument, - nsIContent* aContainer, - PRInt32 aNewIndexInContainer) +nsContentList::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer, + PRInt32 aNewIndexInContainer) { PRInt32 i, count; + aContainer->ChildCount(count); + if ((count > 0) && IsDescendantOfRoot(aContainer)) { PRBool repopulate = PR_FALSE; + for (i = aNewIndexInContainer; i <= count-1; i++) { - nsIContent *content; - aContainer->ChildAt(i, content); + nsCOMPtr content; + aContainer->ChildAt(i, *getter_AddRefs(content)); if (mMatchAll || MatchSelf(content)) { repopulate = PR_TRUE; } - NS_RELEASE(content); } if (repopulate) { PopulateSelf(); @@ -319,7 +573,7 @@ nsContentList::ContentRemoved(nsIDocument *aDocument, NS_IMETHODIMP nsContentList::DocumentWillBeDestroyed(nsIDocument *aDocument) { - if (nsnull != mDocument) { + if (mDocument) { aDocument->RemoveObserver(this); mDocument = nsnull; } @@ -367,48 +621,13 @@ nsContentList::Match(nsIContent *aContent, PRBool *aMatch) *aMatch = PR_TRUE; } } - else if (nsnull != mFunc) { + else if (mFunc) { *aMatch = (*mFunc)(aContent, mData); } return NS_OK; } -nsresult -nsContentList::Add(nsIContent *aContent) -{ - // Shouldn't hold a reference since we'll be - // told when the content leaves the document or - // the document will be destroyed. - mContent.AppendElement(aContent); - - return NS_OK; -} - -nsresult -nsContentList::Remove(nsIContent *aContent) -{ - mContent.RemoveElement(aContent); - - return NS_OK; -} - -nsresult -nsContentList::IndexOf(nsIContent *aContent, PRInt32& aIndex) -{ - aIndex = mContent.IndexOf(aContent); - - return NS_OK; -} - -nsresult -nsContentList::Reset() -{ - mContent.Clear(); - - return NS_OK; -} - // If we were created outside the context of a document and we // have root content, then check if our content has been added // to a document yet. If so, we'll become an observer of the document. @@ -416,9 +635,9 @@ nsresult nsContentList::CheckDocumentExistence() { nsresult result = NS_OK; - if ((nsnull == mDocument) && (nsnull != mRootContent)) { + if (!mDocument && mRootContent) { result = mRootContent->GetDocument(mDocument); - if (nsnull != mDocument) { + if (mDocument) { mDocument->AddObserver(this); PopulateSelf(); } @@ -454,8 +673,7 @@ nsContentList::MatchSelf(nsIContent *aContent) return PR_FALSE; } -// Add all elements in this subtree that match to -// our list. +// Add all elements in this subtree that match to our list. void nsContentList::PopulateWith(nsIContent *aContent, PRBool aIncludeRoot) { @@ -465,7 +683,7 @@ nsContentList::PopulateWith(nsIContent *aContent, PRBool aIncludeRoot) if (aIncludeRoot) { Match(aContent, &match); if (match) { - Add(aContent); + mElements.AppendElement(aContent); } } @@ -483,10 +701,10 @@ void nsContentList::PopulateSelf() { Reset(); - if (nsnull != mRootContent) { + if (mRootContent) { PopulateWith(mRootContent, PR_FALSE); } - else if (nsnull != mDocument) { + else if (mDocument) { nsIContent *root; root = mDocument->GetRootContent(); if (root) { @@ -503,22 +721,21 @@ nsContentList::PopulateSelf() PRBool nsContentList::IsDescendantOfRoot(nsIContent* aContainer) { - if (nsnull == mRootContent) { + if (!mRootContent) { return PR_TRUE; } else if (mRootContent == aContainer) { return PR_TRUE; } - else if (nsnull == aContainer) { + else if (!aContainer) { return PR_FALSE; } else { - nsIContent* parent; + nsCOMPtr parent; PRBool ret; - aContainer->GetParent(parent); + aContainer->GetParent(*getter_AddRefs(parent)); ret = IsDescendantOfRoot(parent); - NS_IF_RELEASE(parent); return ret; } @@ -528,7 +745,7 @@ nsContentList::IsDescendantOfRoot(nsIContent* aContainer) PRBool nsContentList::ContainsRoot(nsIContent* aContent) { - if (nsnull == mRootContent) { + if (!mRootContent) { return PR_FALSE; } else if (mRootContent == aContent) { @@ -539,13 +756,13 @@ nsContentList::ContainsRoot(nsIContent* aContent) aContent->ChildCount(count); for (i = 0; i < count; i++) { - nsIContent *child; - aContent->ChildAt(i, child); + nsCOMPtr child; + + aContent->ChildAt(i, *getter_AddRefs(child)); + if (ContainsRoot(child)) { - NS_RELEASE(child); return PR_TRUE; } - NS_RELEASE(child); } return PR_FALSE; @@ -558,7 +775,7 @@ nsContentList::ContainsRoot(nsIContent* aContent) void nsContentList::DisconnectFromDocument() { - if (nsnull != mDocument) { + if (mDocument) { mDocument->RemoveObserver(this); mDocument = nsnull; } diff --git a/mozilla/content/base/src/nsContentList.h b/mozilla/content/base/src/nsContentList.h index 8d0d557e4bd..38f09579611 100644 --- a/mozilla/content/base/src/nsContentList.h +++ b/mozilla/content/base/src/nsContentList.h @@ -30,15 +30,64 @@ #include "nsIDocumentObserver.h" #include "nsIScriptObjectOwner.h" -typedef PRBool (*nsContentListMatchFunc)(nsIContent* aContent, nsString* aData); +typedef PRBool (*nsContentListMatchFunc)(nsIContent* aContent, + nsString* aData); class nsIDocument; +class nsIDOMHTMLFormElement; -class nsContentList : public nsIDOMNodeList, - public nsIDOMHTMLCollection, - public nsIScriptObjectOwner, - public nsIDocumentObserver { + +class nsBaseContentList : public nsIDOMNodeList, + public nsIScriptObjectOwner +{ public: + nsBaseContentList(); + virtual ~nsBaseContentList(); + + NS_DECL_ISUPPORTS + + // nsIDOMNodeList + NS_DECL_IDOMNODELIST + + // nsIScriptObjectOwner + NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject); + NS_IMETHOD SetScriptObject(void *aScriptObject); + + NS_IMETHOD AppendElement(nsIContent *aContent); + NS_IMETHOD RemoveElement(nsIContent *aContent); + NS_IMETHOD IndexOf(nsIContent *aContent, PRInt32& aIndex); + NS_IMETHOD Reset(); + +protected: + nsVoidArray mElements; + void *mScriptObject; +}; + + +// This class is used only by form element code and this is a static +// list of elements. NOTE! This list holds strong references to +// the elements in the list. +class nsFormContentList : public nsBaseContentList +{ +public: + nsFormContentList(nsIDOMHTMLFormElement *aForm, + nsBaseContentList& aContentList); + virtual ~nsFormContentList(); + + NS_IMETHOD AppendElement(nsIContent *aContent); + NS_IMETHOD RemoveElement(nsIContent *aContent); + + NS_IMETHOD Reset(); +}; + +class nsContentList : public nsBaseContentList, + public nsIDOMHTMLCollection, + public nsIDocumentObserver +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + nsContentList(const nsContentList& aContentList); nsContentList(nsIDocument *aDocument); nsContentList(nsIDocument *aDocument, nsIAtom* aMatchAtom, @@ -46,23 +95,18 @@ public: nsIContent* aRootContent=nsnull); nsContentList(nsIDocument *aDocument, nsContentListMatchFunc aFunc, - const nsString* aData, + const nsAReadableString& aData, nsIContent* aRootContent=nsnull); virtual ~nsContentList(); - NS_DECL_ISUPPORTS - // nsIDOMHTMLCollection NS_IMETHOD GetLength(PRUint32* aLength); - NS_IMETHOD Item(PRUint32 aIndex, nsIDOMNode** aReturn); - NS_IMETHOD NamedItem(const nsAReadableString& aName, nsIDOMNode** aReturn); // nsIScriptObjectOwner NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject); - NS_IMETHOD SetScriptObject(void *aScriptObject); - + // nsIDocumentObserver NS_IMETHOD BeginUpdate(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD EndUpdate(nsIDocument *aDocument) { return NS_OK; } @@ -118,13 +162,8 @@ public: nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument); - nsresult Add(nsIContent *aContent); - nsresult Remove(nsIContent *aContent); - nsresult IndexOf(nsIContent *aContent, PRInt32& aIndex); - protected: nsresult Match(nsIContent *aContent, PRBool *aMatch); - nsresult Reset(); void Init(nsIDocument *aDocument); void PopulateWith(nsIContent *aContent, PRBool aIncludeRoot); PRBool MatchSelf(nsIContent *aContent); @@ -138,8 +177,6 @@ protected: PRInt32 mMatchNameSpaceId; nsContentListMatchFunc mFunc; nsString* mData; - nsVoidArray mContent; - void *mScriptObject; nsIDocument* mDocument; nsIContent* mRootContent; PRBool mMatchAll; diff --git a/mozilla/content/base/src/nsDocument.cpp b/mozilla/content/base/src/nsDocument.cpp index 796eb47ba92..ce3391ff9b1 100644 --- a/mozilla/content/base/src/nsDocument.cpp +++ b/mozilla/content/base/src/nsDocument.cpp @@ -1663,6 +1663,14 @@ nsDocument::ContentRemoved(nsIContent* aContainer, return NS_OK; } +NS_IMETHODIMP +nsDocument::AttributeWillChange(nsIContent* aChild, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute) +{ + return NS_OK; +} + NS_IMETHODIMP nsDocument::AttributeChanged(nsIContent* aChild, PRInt32 aNameSpaceID, @@ -2532,7 +2540,8 @@ nsDocument::GetLocalName(nsAWritableString& aLocalName) } NS_IMETHODIMP -nsDocument::InsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, nsIDOMNode** aReturn) +nsDocument::InsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, + nsIDOMNode** aReturn) { NS_ASSERTION(nsnull != aNewChild, "null ptr"); PRInt32 index; diff --git a/mozilla/content/base/src/nsDocument.h b/mozilla/content/base/src/nsDocument.h index 340e22f770e..e3337b9038e 100644 --- a/mozilla/content/base/src/nsDocument.h +++ b/mozilla/content/base/src/nsDocument.h @@ -305,7 +305,12 @@ public: NS_IMETHOD EndLoad(); NS_IMETHOD ContentChanged(nsIContent* aContent, nsISupports* aSubContent); - NS_IMETHOD ContentStatesChanged(nsIContent* aContent1, nsIContent* aContent2); + NS_IMETHOD ContentStatesChanged(nsIContent* aContent1, + nsIContent* aContent2); + + NS_IMETHOD AttributeWillChange(nsIContent* aChild, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute); NS_IMETHOD AttributeChanged(nsIContent* aChild, PRInt32 aNameSpaceID, nsIAtom* aAttribute, diff --git a/mozilla/content/base/src/nsGenericDOMDataNode.cpp b/mozilla/content/base/src/nsGenericDOMDataNode.cpp index ce7f2595053..e00c0b11763 100644 --- a/mozilla/content/base/src/nsGenericDOMDataNode.cpp +++ b/mozilla/content/base/src/nsGenericDOMDataNode.cpp @@ -58,7 +58,6 @@ nsGenericDOMDataNode::nsGenericDOMDataNode() mScriptObject = nsnull; mListenerManager = nsnull; mRangeList = nsnull; - mCapturer = nsnull; } nsGenericDOMDataNode::~nsGenericDOMDataNode() diff --git a/mozilla/content/base/src/nsGenericDOMDataNode.h b/mozilla/content/base/src/nsGenericDOMDataNode.h index 60694c8523c..c30384e500f 100644 --- a/mozilla/content/base/src/nsGenericDOMDataNode.h +++ b/mozilla/content/base/src/nsGenericDOMDataNode.h @@ -264,7 +264,6 @@ struct nsGenericDOMDataNode { nsIContent* mParent; void* mScriptObject; nsIEventListenerManager* mListenerManager; - nsIContent* mCapturer; nsTextFragment mText; nsVoidArray *mRangeList; diff --git a/mozilla/content/base/src/nsGenericElement.cpp b/mozilla/content/base/src/nsGenericElement.cpp index 3dc9b548e5c..d92e94d7937 100644 --- a/mozilla/content/base/src/nsGenericElement.cpp +++ b/mozilla/content/base/src/nsGenericElement.cpp @@ -428,7 +428,6 @@ nsGenericElement::GetDOMSlots() mDOMSlots->mStyle = nsnull; mDOMSlots->mAttributeMap = nsnull; mDOMSlots->mRangeList = nsnull; - mDOMSlots->mCapturer = nsnull; mDOMSlots->mListenerManager = nsnull; mDOMSlots->mBindingParent = nsnull; } @@ -445,7 +444,6 @@ nsGenericElement::MaybeClearDOMSlots() !mDOMSlots->mStyle && !mDOMSlots->mAttributeMap && !mDOMSlots->mRangeList && - !mDOMSlots->mCapturer && !mDOMSlots->mListenerManager && !mDOMSlots->mBindingParent) { @@ -2958,8 +2956,16 @@ nsGenericContainerElement::SetAttribute(nsINodeInfo* aNodeInfo, NS_ENSURE_TRUE(mAttributes, NS_ERROR_OUT_OF_MEMORY); } + nsCOMPtr name; + PRInt32 nameSpaceID; + + aNodeInfo->GetNameAtom(*getter_AddRefs(name)); + aNodeInfo->GetNamespaceID(nameSpaceID); + if (aNotify && (nsnull != mDocument)) { mDocument->BeginUpdate(); + + mDocument->AttributeWillChange(this, nameSpaceID, name); } nsGenericAttribute* attr; @@ -2985,12 +2991,6 @@ nsGenericContainerElement::SetAttribute(nsINodeInfo* aNodeInfo, } if (mDocument && NS_SUCCEEDED(rv)) { - nsCOMPtr name; - PRInt32 nameSpaceID; - - aNodeInfo->GetNameAtom(*getter_AddRefs(name)); - aNodeInfo->GetNamespaceID(nameSpaceID); - nsCOMPtr bindingManager; mDocument->GetBindingManager(getter_AddRefs(bindingManager)); nsCOMPtr binding; @@ -3108,6 +3108,8 @@ nsGenericContainerElement::UnsetAttribute(PRInt32 aNameSpaceID, attr->mNodeInfo->Equals(aName)) { if (aNotify && (nsnull != mDocument)) { mDocument->BeginUpdate(); + + mDocument->AttributeWillChange(this, aNameSpaceID, aName); } if (HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED)) { diff --git a/mozilla/content/base/src/nsGenericElement.h b/mozilla/content/base/src/nsGenericElement.h index 82805fede5c..6b67ac19f24 100644 --- a/mozilla/content/base/src/nsGenericElement.h +++ b/mozilla/content/base/src/nsGenericElement.h @@ -122,7 +122,6 @@ typedef struct { nsDOMCSSDeclaration *mStyle; nsDOMAttributeMap* mAttributeMap; nsVoidArray *mRangeList; - nsIContent* mCapturer; nsIEventListenerManager* mListenerManager; nsIContent* mBindingParent; // The nearest enclosing content node with a binding // that created us. [Weak] diff --git a/mozilla/content/html/content/public/nsIFormControl.h b/mozilla/content/html/content/public/nsIFormControl.h index 536a507747d..ffe38d86307 100644 --- a/mozilla/content/html/content/public/nsIFormControl.h +++ b/mozilla/content/html/content/public/nsIFormControl.h @@ -76,7 +76,8 @@ public: * @param aForm the form * @return NS_OK */ - NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm) = 0; + NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm, + PRBool aRemoveFromForm = PR_TRUE) = 0; /** * Get the type of this control diff --git a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp index eec3e9f3c24..044cde4586f 100644 --- a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp +++ b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp @@ -164,9 +164,13 @@ nsDOMCSSAttributeDeclaration::RemoveProperty(const nsAReadableString& aPropertyN nsCOMPtr doc; mContent->GetDocument(*getter_AddRefs(doc)); - if (doc) + if (doc) { doc->BeginUpdate(); + doc->AttributeWillChange(mContent, kNameSpaceID_None, + nsHTMLAtoms::style); + } + PRInt32 hint; decl->GetStyleImpact(&hint); @@ -203,7 +207,7 @@ nsDOMCSSAttributeDeclaration::DropReference() nsresult nsDOMCSSAttributeDeclaration::GetCSSDeclaration(nsICSSDeclaration **aDecl, - PRBool aAllocate) + PRBool aAllocate) { nsHTMLValue val; nsIStyleRule* rule; @@ -306,6 +310,9 @@ nsDOMCSSAttributeDeclaration::ParseDeclaration(const nsAReadableString& aDecl, PRInt32 hint; if (doc) { doc->BeginUpdate(); + + doc->AttributeWillChange(mContent, kNameSpaceID_None, + nsHTMLAtoms::style); } nsCOMPtr declClone; decl->Clone(*getter_AddRefs(declClone)); @@ -335,7 +342,8 @@ nsDOMCSSAttributeDeclaration::ParseDeclaration(const nsAReadableString& aDecl, } if (doc) { if (NS_SUCCEEDED(result) && result != NS_CSS_PARSER_DROP_DECLARATION) { - doc->AttributeChanged(mContent, kNameSpaceID_None, nsHTMLAtoms::style, hint); + doc->AttributeChanged(mContent, kNameSpaceID_None, + nsHTMLAtoms::style, hint); } doc->EndUpdate(); } @@ -908,9 +916,11 @@ nsGenericHTMLElement::SetInnerHTML(const nsAReadableString& aInnerHTML) { nsresult rv = NS_OK; - nsRange *range = new nsRange; + nsCOMPtr range = new nsRange; NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY); - NS_ADDREF(range); + + nsCOMPtr nsrange(do_QueryInterface(range, &rv)); + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr thisNode(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this))); @@ -922,11 +932,9 @@ nsGenericHTMLElement::SetInnerHTML(const nsAReadableString& aInnerHTML) nsCOMPtr df; - rv = range->CreateContextualFragment(aInnerHTML, getter_AddRefs(df)); + rv = nsrange->CreateContextualFragment(aInnerHTML, getter_AddRefs(df)); NS_ENSURE_SUCCESS(rv, rv); - NS_RELEASE(range); - nsCOMPtr tmpNode; return thisNode->AppendChild(df, getter_AddRefs(tmpNode)); } @@ -1328,6 +1336,8 @@ nsGenericHTMLElement::SetAttribute(PRInt32 aNameSpaceID, if (aNotify && (nsnull != mDocument)) { mDocument->BeginUpdate(); + + mDocument->AttributeWillChange(this, aNameSpaceID, aAttribute); } // set as string value to avoid another string copy @@ -1452,10 +1462,14 @@ nsGenericHTMLElement::SetHTMLAttribute(nsIAtom* aAttribute, if (mDocument) { if (aNotify) { mDocument->BeginUpdate(); + + mDocument->AttributeWillChange(this, kNameSpaceID_None, aAttribute); + if (nsHTMLAtoms::style == aAttribute) { nsHTMLValue oldValue; PRInt32 oldImpact = NS_STYLE_HINT_NONE; - if (NS_CONTENT_ATTR_NOT_THERE != GetHTMLAttribute(aAttribute, oldValue)) { + if (NS_CONTENT_ATTR_NOT_THERE != GetHTMLAttribute(aAttribute, + oldValue)) { oldImpact = GetStyleImpactFrom(oldValue); } impact = GetStyleImpactFrom(aValue); @@ -1548,9 +1562,13 @@ nsGenericHTMLElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 impact = NS_STYLE_HINT_UNKNOWN; if (aNotify) { mDocument->BeginUpdate(); + + mDocument->AttributeWillChange(this, aNameSpaceID, aAttribute); + if (nsHTMLAtoms::style == aAttribute) { nsHTMLValue oldValue; - if (NS_CONTENT_ATTR_NOT_THERE != GetHTMLAttribute(aAttribute, oldValue)) { + if (NS_CONTENT_ATTR_NOT_THERE != GetHTMLAttribute(aAttribute, + oldValue)) { impact = GetStyleImpactFrom(oldValue); } else { @@ -3581,20 +3599,24 @@ nsGenericHTMLContainerFormElement::QueryInterface(REFNSIID aIID, } NS_IMETHODIMP -nsGenericHTMLContainerFormElement::SetForm(nsIDOMHTMLFormElement* aForm) +nsGenericHTMLContainerFormElement::SetForm(nsIDOMHTMLFormElement* aForm, + PRBool aRemoveFromForm) { nsAutoString nameVal, idVal; - GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, nameVal); - GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, idVal); - if (mForm) { - mForm->RemoveElement(this); + if (aRemoveFromForm) { + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, nameVal); + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, idVal); - if (!nameVal.IsEmpty()) - mForm->RemoveElementFromTable(this, nameVal); + if (mForm) { + mForm->RemoveElement(this); - if (!idVal.IsEmpty()) - mForm->RemoveElementFromTable(this, idVal); + if (!nameVal.IsEmpty()) + mForm->RemoveElementFromTable(this, nameVal); + + if (!idVal.IsEmpty()) + mForm->RemoveElementFromTable(this, idVal); + } } if (aForm) { @@ -3720,26 +3742,85 @@ nsGenericHTMLContainerFormElement::SetDocument(nsIDocument* aDocument, return rv; } +nsresult +nsGenericHTMLElement::SetFormControlAttribute(nsIForm* aForm, + PRInt32 aNameSpaceID, + nsIAtom* aName, + const nsAReadableString& aValue, + PRBool aNotify) +{ + nsCOMPtr thisControl; + nsAutoString tmp; + nsresult rv = NS_OK; + + QueryInterface(NS_GET_IID(nsIFormControl), getter_AddRefs(thisControl)); + + // Add & remove the control to and/or from the hash table + if (aForm && (aName == nsHTMLAtoms::name || aName == nsHTMLAtoms::id)) { + GetAttribute(kNameSpaceID_None, aName, tmp); + + if (!tmp.IsEmpty()) { + aForm->RemoveElementFromTable(thisControl, tmp); + } + + aForm->RemoveElement(thisControl); + } + + if (aForm && aName == nsHTMLAtoms::type) { + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, tmp); + + if (!tmp.IsEmpty()) { + aForm->RemoveElementFromTable(thisControl, tmp); + } + + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, tmp); + + if (!tmp.IsEmpty()) { + aForm->RemoveElementFromTable(thisControl, tmp); + } + + aForm->RemoveElement(thisControl); + } + + rv = nsGenericHTMLElement::SetAttribute(aNameSpaceID, aName, aValue, + aNotify); + + if (aForm && (aName == nsHTMLAtoms::name || aName == nsHTMLAtoms::id)) { + GetAttribute(kNameSpaceID_None, aName, tmp); + + if (!tmp.IsEmpty()) { + aForm->AddElementToTable(thisControl, tmp); + } + + aForm->AddElement(thisControl); + } + + if (aForm && aName == nsHTMLAtoms::type) { + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, tmp); + + if (!tmp.IsEmpty()) { + aForm->AddElementToTable(thisControl, tmp); + } + + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, tmp); + + if (!tmp.IsEmpty()) { + aForm->AddElementToTable(thisControl, tmp); + } + + aForm->AddElement(thisControl); + } + + return rv; +} + NS_IMETHODIMP nsGenericHTMLContainerFormElement::SetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, - const nsAReadableString& aValue, + const nsAReadableString& aVal, PRBool aNotify) { - // Add & remove the control to and/or from the hash table - if (mForm && (nsHTMLAtoms::name == aName || nsHTMLAtoms::id == aName)) { - nsAutoString tmp; - nsresult rv = GetAttribute(kNameSpaceID_None, aName, tmp); - - if (rv == NS_CONTENT_ATTR_HAS_VALUE) { - mForm->RemoveElementFromTable(this, tmp); - } - - mForm->AddElementToTable(this, aValue); - } - - return nsGenericHTMLElement::SetAttribute(aNameSpaceID, aName, aValue, - aNotify); + return SetFormControlAttribute(mForm, aNameSpaceID, aName, aVal, aNotify); } //---------------------------------------------------------------------- @@ -3778,20 +3859,24 @@ nsGenericHTMLLeafFormElement::QueryInterface(REFNSIID aIID, } NS_IMETHODIMP -nsGenericHTMLLeafFormElement::SetForm(nsIDOMHTMLFormElement* aForm) +nsGenericHTMLLeafFormElement::SetForm(nsIDOMHTMLFormElement* aForm, + PRBool aRemoveFromForm) { nsAutoString nameVal, idVal; - GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, nameVal); - GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, idVal); - if (mForm) { - mForm->RemoveElement(this); + if (aRemoveFromForm) { + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, nameVal); + GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, idVal); - if (nameVal.Length()) - mForm->RemoveElementFromTable(this, nameVal); + if (mForm) { + mForm->RemoveElement(this); - if (idVal.Length()) - mForm->RemoveElementFromTable(this, idVal); + if (nameVal.Length()) + mForm->RemoveElementFromTable(this, nameVal); + + if (idVal.Length()) + mForm->RemoveElementFromTable(this, idVal); + } } if (aForm) { @@ -3931,20 +4016,7 @@ nsGenericHTMLLeafFormElement::SetAttribute(PRInt32 aNameSpaceID, const nsAReadableString& aValue, PRBool aNotify) { - // Add & remove the control to and/or from the hash table - if (mForm && (nsHTMLAtoms::name == aName || nsHTMLAtoms::id == aName)) { - nsAutoString tmp; - nsresult rv = GetAttribute(kNameSpaceID_None, aName, tmp); - - if (rv == NS_CONTENT_ATTR_HAS_VALUE) { - mForm->RemoveElementFromTable(this, tmp); - } - - mForm->AddElementToTable(this, aValue); - } - - return nsGenericHTMLElement::SetAttribute(aNameSpaceID, aName, aValue, - aNotify); + return SetFormControlAttribute(mForm, aNameSpaceID, aName, aValue, aNotify); } nsresult diff --git a/mozilla/content/html/content/src/nsGenericHTMLElement.h b/mozilla/content/html/content/src/nsGenericHTMLElement.h index 78544074814..1fd39df0f3d 100644 --- a/mozilla/content/html/content/src/nsGenericHTMLElement.h +++ b/mozilla/content/html/content/src/nsGenericHTMLElement.h @@ -340,6 +340,11 @@ public: // mode enabled. static PRBool InNavQuirksMode(nsIDocument* aDoc); + nsresult SetFormControlAttribute(nsIForm* aForm, PRInt32 aNameSpaceID, + nsIAtom* aName, + const nsAReadableString& aValue, + PRBool aNotify); + nsIHTMLAttributes* mAttributes; protected: @@ -489,7 +494,8 @@ public: // nsIFormControl NS_IMETHOD GetForm(nsIDOMHTMLFormElement** aForm); - NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm); + NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm, + PRBool aRemoveFromForm = PR_TRUE); NS_IMETHOD Init(); NS_IMETHOD SetParent(nsIContent *aParent); @@ -523,7 +529,8 @@ public: // nsIFormControl NS_IMETHOD GetForm(nsIDOMHTMLFormElement** aForm); - NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm); + NS_IMETHOD SetForm(nsIDOMHTMLFormElement* aForm, + PRBool aRemoveFromForm = PR_TRUE); NS_IMETHOD Init(); NS_IMETHOD SetParent(nsIContent *aParent); diff --git a/mozilla/content/html/content/src/nsHTMLFormElement.cpp b/mozilla/content/html/content/src/nsHTMLFormElement.cpp index 5a766d05f51..a55756813d1 100644 --- a/mozilla/content/html/content/src/nsHTMLFormElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLFormElement.cpp @@ -26,7 +26,7 @@ #include "nsIDOMHTMLFormElement.h" #include "nsIDOMNSHTMLFormElement.h" #include "nsIDOMHTMLFormControlList.h" -#include "nsIDOMHTMLDocument.h" +#include "nsIHTMLDocument.h" #include "nsIScriptObjectOwner.h" #include "nsIDOMEventReceiver.h" #include "nsIHTMLContent.h" @@ -47,7 +47,7 @@ #include "nsHashtable.h" #include "nsContentList.h" -static const int NS_FORM_CONTROL_LIST_HASHTABLE_SIZE = 64; +static const int NS_FORM_CONTROL_LIST_HASHTABLE_SIZE = 16; class nsFormControlList; @@ -141,14 +141,69 @@ public: #endif void *mScriptObject; - nsVoidArray mElements; // WEAK - bug 36639 nsIDOMHTMLFormElement* mForm; // WEAK - the form owns me + nsVoidArray mElements; // Holds WEAK references - bug 36639 + + // This hash holds on to all form controls that are not named form + // control (see IsNamedFormControl()), this is needed to properly + // clean up the bi-directional references (both weak and strong) + // between the form and its form controls. + + nsHashtable* mNoNameLookupTable; // Holds WEAK references + protected: - // A map from an ID or NAME attribute to the form control(s) - nsSupportsHashtable* mLookupTable; + // A map from an ID or NAME attribute to the form control(s), this + // hash holds strong references either to the named form control, or + // to a list of named form controls, in the case where this hash + // holds on to a list of named form controls the list has weak + // references to the form control. + + nsSupportsHashtable mNameLookupTable; }; +static PRBool +IsNamedFormControl(nsIFormControl* aFormControl) +{ + PRInt32 type; + + aFormControl->GetType(&type); + + // For backwards compatibility (with 4.x and IE) we must not add + // elements to the list of form controls in a + // form. + + switch (type) { + case NS_FORM_BUTTON_BUTTON : + case NS_FORM_BUTTON_RESET : + case NS_FORM_BUTTON_SUBMIT : + case NS_FORM_INPUT_BUTTON : + case NS_FORM_INPUT_CHECKBOX : + case NS_FORM_INPUT_FILE : + case NS_FORM_INPUT_HIDDEN : + case NS_FORM_INPUT_RESET : + case NS_FORM_INPUT_PASSWORD : + case NS_FORM_INPUT_RADIO : + case NS_FORM_INPUT_SUBMIT : + case NS_FORM_INPUT_TEXT : + case NS_FORM_SELECT : + case NS_FORM_TEXTAREA : + case NS_FORM_FIELDSET : + return PR_TRUE; + } + + // These form control types are not supposed to end up in the + // form.elements array + // + // NS_FORM_INPUT_IMAGE + // NS_FORM_LABEL + // NS_FORM_OPTION + // NS_FORM_OPTGROUP + // NS_FORM_LEGEND + + return PR_FALSE; +} + // nsHTMLFormElement implementation // construction, destruction @@ -185,19 +240,16 @@ nsHTMLFormElement::nsHTMLFormElement() NS_IF_ADDREF(mControls); } + + nsHTMLFormElement::~nsHTMLFormElement() { - // Null out childrens' pointer to me. No refcounting here - PRUint32 numControls; - GetElementCount(&numControls); - while (numControls--) { - nsIFormControl* control = (nsIFormControl*)mControls->mElements.ElementAt(numControls); - if (control) { - control->SetForm(nsnull); - } + if (mControls) { + mControls->Clear(); + mControls->SetForm(nsnull); + + NS_RELEASE(mControls); } - mControls->SetForm(nsnull); - NS_RELEASE(mControls); } @@ -469,15 +521,36 @@ nsHTMLFormElement::GetElementAt(PRInt32 aIndex, NS_IMETHODIMP nsHTMLFormElement::AddElement(nsIFormControl* aChild) { - PRBool rv = mControls->mElements.AppendElement(aChild); - // WEAK - don't addref - return rv; + NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED); + + if (IsNamedFormControl(aChild)) { + mControls->mElements.AppendElement(aChild); + // WEAK - don't addref + } else { + if (!mControls->mNoNameLookupTable) { + mControls->mNoNameLookupTable = new nsHashtable(); + NS_ENSURE_TRUE(mControls->mNoNameLookupTable, NS_ERROR_OUT_OF_MEMORY); + } + + nsISupportsKey key(aChild); + + nsISupports *item = + NS_STATIC_CAST(nsISupports *, mControls->mNoNameLookupTable->Get(&key)); + + if (!item) { + mControls->mNoNameLookupTable->Put(&key, aChild); + } + } + + return NS_OK; } NS_IMETHODIMP nsHTMLFormElement::AddElementToTable(nsIFormControl* aChild, const nsAReadableString& aName) { + NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED); + return mControls->AddElementToTable(aChild, aName); } @@ -485,8 +558,16 @@ nsHTMLFormElement::AddElementToTable(nsIFormControl* aChild, NS_IMETHODIMP nsHTMLFormElement::RemoveElement(nsIFormControl* aChild) { + NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED); + mControls->mElements.RemoveElement(aChild); + if (mControls->mNoNameLookupTable) { + nsISupportsKey key(aChild); + + mControls->mNoNameLookupTable->Remove(&key); + } + return NS_OK; } @@ -494,6 +575,8 @@ NS_IMETHODIMP nsHTMLFormElement::RemoveElementFromTable(nsIFormControl* aElement, const nsAReadableString& aName) { + NS_ENSURE_TRUE(mControls, NS_ERROR_UNEXPECTED); + return mControls->RemoveElementFromTable(aElement, aName); } @@ -537,42 +620,28 @@ nsHTMLFormElement::NamedItem(JSContext* cx, jsval* argv, PRUint32 argc, result = globalObject->GetContext(getter_AddRefs(scriptContext)); } - nsCOMPtr htmlDoc(do_QueryInterface(mDocument)); + nsCOMPtr html_doc(do_QueryInterface(mDocument)); - if (htmlDoc) { - nsCOMPtr list; + if (html_doc) { + nsCOMPtr item; - result = htmlDoc->GetElementsByName(nsLiteralString(str), - getter_AddRefs(list)); + result = html_doc->ResolveName(nsLiteralString(str), this, + getter_AddRefs(item)); if (NS_FAILED(result)) { return result; } - - if (list) { - PRUint32 count; - list->GetLength(&count); - if (count > 0) { - nsCOMPtr node; - - result = list->Item(0, getter_AddRefs(node)); + nsCOMPtr owner(do_QueryInterface(item)); - if (NS_FAILED(result)) { - return result; - } + if (owner) { + JSObject* obj; - if (node) { - nsCOMPtr owner = do_QueryInterface(node); - JSObject* obj; - - result = owner->GetScriptObject(scriptContext, (void**)&obj); - if (NS_FAILED(result)) { - return result; - } - - *aReturn = OBJECT_TO_JSVAL(obj); - } + result = owner->GetScriptObject(scriptContext, (void**)&obj); + if (NS_FAILED(result)) { + return result; } + + *aReturn = OBJECT_TO_JSVAL(obj); } } } @@ -609,55 +678,25 @@ nsHTMLFormElement::Resolve(JSContext *aContext, JSObject *aObj, jsval aID, } if (!obj && mDocument) { - nsCOMPtr htmlDoc(do_QueryInterface(mDocument)); + nsCOMPtr html_doc(do_QueryInterface(mDocument)); - if (htmlDoc) { - nsCOMPtr list; + if (html_doc) { + nsCOMPtr item; - rv = htmlDoc->GetElementsByName(nsLiteralString(NS_REINTERPRET_CAST(PRUnichar *, str), str_len), getter_AddRefs(list)); + nsLiteralString name(NS_REINTERPRET_CAST(PRUnichar *, str), str_len); + + rv = html_doc->ResolveName(name, this, getter_AddRefs(item)); if (NS_FAILED(rv)) { return PR_FALSE; } - if (list) { - PRUint32 count; - list->GetLength(&count); + nsCOMPtr owner(do_QueryInterface(item)); - if (count > 0) { - nsCOMPtr node; + if (owner) { + rv = owner->GetScriptObject(scriptContext, (void**)&obj); - rv = list->Item(0, getter_AddRefs(node)); - if (NS_FAILED(rv)) { - return PR_FALSE; - } - - nsCOMPtr formControl(do_QueryInterface(node)); - - // If htmlDoc->GetElementsByName() found a formcontrol we - // check to see if the formcontrol is part of this form, if - // it's not we don't define the property here... - if (formControl) { - nsCOMPtr formElement; - - formControl->GetForm(getter_AddRefs(formElement)); - - if (formElement.get() != NS_STATIC_CAST(nsIDOMHTMLFormElement *, - this)) { - // Set node to null so that we don't define the property - // here... - node = nsnull; - } - } - - if (node) { - nsCOMPtr owner(do_QueryInterface(node)); - - rv = owner->GetScriptObject(scriptContext, (void**)&obj); - - if (NS_FAILED(rv)) { - return PR_FALSE; - } - } + if (NS_FAILED(rv)) { + return PR_FALSE; } } } @@ -688,8 +727,7 @@ nsHTMLFormElement::Item(PRUint32 aIndex, nsIDOMElement** aReturn) nsresult result = mControls->Item(aIndex, getter_AddRefs(node)); if (node) { - result = node->QueryInterface(NS_GET_IID(nsIDOMElement), - (void **)aReturn); + result = CallQueryInterface(node, aReturn); } else { *aReturn = nsnull; } @@ -705,21 +743,19 @@ nsHTMLFormElement::Item(PRUint32 aIndex, nsIDOMElement** aReturn) // nsFormControlList implementation, this could go away if there were // a lightweight collection implementation somewhere - -nsFormControlList::nsFormControlList(nsIDOMHTMLFormElement* aForm) +nsFormControlList::nsFormControlList(nsIDOMHTMLFormElement* aForm) + : mScriptObject(nsnull), mForm(aForm), + mNameLookupTable(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE), + mNoNameLookupTable(nsnull) { NS_INIT_REFCNT(); - mScriptObject = nsnull; - mForm = aForm; // WEAK - the form owns me - mLookupTable = nsnull; } nsFormControlList::~nsFormControlList() { - if (mLookupTable) { - delete mLookupTable; - mLookupTable = nsnull; - } + delete mNoNameLookupTable; + mNoNameLookupTable = nsnull; + mForm = nsnull; Clear(); } @@ -737,13 +773,38 @@ nsFormControlList::SetForm(nsIDOMHTMLFormElement* aForm) mForm = aForm; // WEAK - the form owns me } +static PRBool FormControlResetEnumFunction(nsHashKey *aKey, void *aData, + void* closure) +{ + nsIFormControl *f = NS_STATIC_CAST(nsIFormControl *, aData); + + f->SetForm(nsnull, PR_FALSE); + + return PR_TRUE; +} + void nsFormControlList::Clear() { + // Null out childrens' pointer to me. No refcounting here + PRInt32 numControls = mElements.Count(); + + while (numControls-- > 0) { + nsIFormControl* f = NS_STATIC_CAST(nsIFormControl *, + mElements.ElementAt(numControls)); + + if (f) { + f->SetForm(nsnull, PR_FALSE); + } + } + mElements.Clear(); - if (mLookupTable) - mLookupTable->Reset(); + mNameLookupTable.Reset(); + + if (mNoNameLookupTable) { + mNoNameLookupTable->Reset(FormControlResetEnumFunction); + } } NS_IMPL_ADDREF(nsFormControlList) @@ -793,8 +854,7 @@ nsFormControlList::Item(PRUint32 aIndex, nsIDOMNode** aReturn) nsIFormControl *control = (nsIFormControl*)mElements.ElementAt(aIndex); if (control) { - return control->QueryInterface(NS_GET_IID(nsIDOMNode), - (void**)aReturn); + return CallQueryInterface(control, aReturn); } *aReturn = nsnull; @@ -885,19 +945,17 @@ nsFormControlList::GetNamedObject(JSContext* aContext, jsval aID, nsCOMPtr scriptContext; nsCOMPtr owner; char* str = JS_GetStringBytes(JS_ValueToString(aContext, aID)); + nsAutoString ustr; ustr.AssignWithConversion(str); - if (mLookupTable) { - // Get the hash entry - nsAutoString ustr; ustr.AssignWithConversion(str); - nsStringKey key(ustr); + // Get the hash entry + nsStringKey key(ustr); - nsCOMPtr tmp = dont_AddRef((nsISupports *)mLookupTable->Get(&key)); + nsCOMPtr item(dont_AddRef(mNameLookupTable.Get(&key))); - if (tmp) { - // Found something, we don't care here if it's a element or a node - // list, we just return the script object - owner = do_QueryInterface(tmp); - } + if (item) { + // Found something, we don't care here if it's a element or a node + // list, we just return the script object + owner = do_QueryInterface(item); } if (!owner) { @@ -960,16 +1018,13 @@ nsFormControlList::NamedItem(const nsAReadableString& aName, nsresult rv = NS_OK; nsStringKey key(aName); - nsCOMPtr supports; *aReturn = nsnull; - if (mLookupTable) { - supports = dont_AddRef((nsISupports *)mLookupTable->Get(&key)); - } + nsCOMPtr supports(dont_AddRef(mNameLookupTable.Get(&key))); if (supports) { // We found something, check if it's a node - rv = supports->QueryInterface(NS_GET_IID(nsIDOMNode), (void **)aReturn); + rv = CallQueryInterface(supports, aReturn); if (NS_FAILED(rv)) { // If not, we check if it's a node list. @@ -990,20 +1045,34 @@ nsresult nsFormControlList::AddElementToTable(nsIFormControl* aChild, const nsAReadableString& aName) { - nsStringKey key(aName); - if (!mLookupTable) { - mLookupTable = new nsSupportsHashtable(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE); - NS_ENSURE_TRUE(mLookupTable, NS_ERROR_OUT_OF_MEMORY); + if (!IsNamedFormControl(aChild)) { + if (!mNoNameLookupTable) { + mNoNameLookupTable = new nsHashtable(); + NS_ENSURE_TRUE(mNoNameLookupTable, NS_ERROR_OUT_OF_MEMORY); + } + + nsISupportsKey key(aChild); + + nsISupports *item = NS_STATIC_CAST(nsISupports *, + mNoNameLookupTable->Get(&key)); + + if (!item) { + mNoNameLookupTable->Put(&key, aChild); + } + + return NS_OK; } + nsStringKey key(aName); + nsCOMPtr supports; - supports = dont_AddRef((nsISupports *)mLookupTable->Get(&key)); + supports = dont_AddRef(mNameLookupTable.Get(&key)); if (!supports) { // No entry found, add the form control nsCOMPtr child(do_QueryInterface(aChild)); - mLookupTable->Put(&key, child.get()); + mNameLookupTable.Put(&key, child); } else { // Found something in the hash, check its type nsCOMPtr content(do_QueryInterface(supports)); @@ -1023,16 +1092,21 @@ nsFormControlList::AddElementToTable(nsIFormControl* aChild, nsContentList *list = new nsContentList(nsnull); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); - list->Add(content); + list->AppendElement(content); // Add the new child too - list->Add(newChild); + list->AppendElement(newChild); nsCOMPtr listSupports; list->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(listSupports)); + + // Remove the current item from the hash so that we don't create + // a leak when we add the new item to the hash. + mNameLookupTable.Remove(&key); + // Replace the element with the list. - mLookupTable->Put(&key, listSupports.get()); + mNameLookupTable.Put(&key, listSupports); } else { // There's already a list in the hash, add the child to the list nsCOMPtr nodeList(do_QueryInterface(supports)); @@ -1047,7 +1121,7 @@ nsFormControlList::AddElementToTable(nsIFormControl* aChild, // Add the new child only if it's not in our list already if (oldIndex < 0) { - list->Add(newChild); + list->AppendElement(newChild); } } } @@ -1059,51 +1133,65 @@ nsresult nsFormControlList::RemoveElementFromTable(nsIFormControl* aChild, const nsAReadableString& aName) { + if (!IsNamedFormControl(aChild)) { + if (mNoNameLookupTable) { + nsISupportsKey key(aChild); + + mNoNameLookupTable->Remove(&key); + } + + return NS_OK; + } + nsCOMPtr content = do_QueryInterface(aChild); - if (mLookupTable && content) { - nsStringKey key(aName); + if (!content) { + return NS_OK; + } - nsCOMPtr supports; - supports = dont_AddRef((nsISupports *)mLookupTable->Get(&key)); + nsStringKey key(aName); - if (!supports) - return NS_OK; + nsCOMPtr supports(dont_AddRef(mNameLookupTable.Get(&key))); - nsCOMPtr fctrl(do_QueryInterface(supports)); + if (!supports) + return NS_OK; - if (fctrl) { - // Single element in the hash, just remove it... - mLookupTable->Remove(&key); - } else { - nsCOMPtr nodeList(do_QueryInterface(supports)); - NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); + nsCOMPtr fctrl(do_QueryInterface(supports)); - // Upcast, uggly, but it works! - nsContentList *list = NS_STATIC_CAST(nsContentList *, - (nsIDOMNodeList *)nodeList.get()); + if (fctrl) { + // Single element in the hash, just remove it... + mNameLookupTable.Remove(&key); - list->Remove(content); + return NS_OK; + } - PRUint32 length = 0; - list->GetLength(&length); + nsCOMPtr nodeList(do_QueryInterface(supports)); + NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); - if (!length) { - // If the list is empty we remove if from our hash, this shouldn't - // happen tho - mLookupTable->Remove(&key); - } else if (length == 1) { - // Only one element left, replace the list in the hash with the - // single element. - nsCOMPtr node; - list->Item(0, getter_AddRefs(node)); + // Upcast, uggly, but it works! + nsContentList *list = NS_STATIC_CAST(nsContentList *, + (nsIDOMNodeList *)nodeList.get()); - if (node) { - nsCOMPtr tmp(do_QueryInterface(node)); - mLookupTable->Put(&key, tmp.get()); - } - } + list->RemoveElement(content); + + PRUint32 length = 0; + list->GetLength(&length); + + if (!length) { + // If the list is empty we remove if from our hash, this shouldn't + // happen tho + mNameLookupTable.Remove(&key); + } else if (length == 1) { + // Only one element left, replace the list in the hash with the + // single element. + nsCOMPtr node; + list->Item(0, getter_AddRefs(node)); + + if (node) { + nsCOMPtr tmp(do_QueryInterface(node)); + mNameLookupTable.Put(&key, tmp.get()); } } + return NS_OK; } diff --git a/mozilla/content/html/content/src/nsHTMLUnknownElement.cpp b/mozilla/content/html/content/src/nsHTMLUnknownElement.cpp index 0b708e5ef1a..862c6c566af 100644 --- a/mozilla/content/html/content/src/nsHTMLUnknownElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLUnknownElement.cpp @@ -239,6 +239,8 @@ nsHTMLUnknownElement::SetAttribute(PRInt32 aNameSpaceID, if (aNotify && (mDocument)) { mDocument->BeginUpdate(); + + mDocument->AttributeWillChange(this, aNameSpaceID, aAttribute); } // set as string value to avoid another string copy diff --git a/mozilla/content/html/document/src/nsHTMLDocument.cpp b/mozilla/content/html/document/src/nsHTMLDocument.cpp index 1ae4afbda21..ec3d6031588 100644 --- a/mozilla/content/html/document/src/nsHTMLDocument.cpp +++ b/mozilla/content/html/document/src/nsHTMLDocument.cpp @@ -37,6 +37,7 @@ #include "nsIHTMLCSSStyleSheet.h" #include "nsIStyleSet.h" #include "nsHTMLAtoms.h" +#include "nsLayoutAtoms.h" #include "nsIPresShell.h" #include "nsIPresContext.h" #include "nsIHTMLContent.h" @@ -139,15 +140,16 @@ const PRInt32 kBackward = 1; #include "nsHTMLContentSinkStream.h" #endif -// XXX Used to control whether we implement document.layers -//#define NS_IMPLEMENT_DOCUMENT_LAYERS - +#define ELEMENT_NOT_IN_TABLE ((nsIContent *)1) static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); static NS_DEFINE_CID(kCookieServiceCID, NS_COOKIESERVICE_CID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); +static PRBool +IsNamedItem(nsIContent* aContent, nsIAtom *aTag, nsAWritableString& aName); + static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); static NS_DEFINE_CID(kHTMLStyleSheetCID,NS_HTMLSTYLESHEET_CID); @@ -178,58 +180,6 @@ MyPrefChangedCallback(const char*aPrefName, void* instance_data) return 0; } -// These functions are copied from nsprpub/lib/ds/plhash.c, with one -// change to free the string used as a key. - -static void * PR_CALLBACK -NamedItemsAllocTable(void *pool, PRSize size) -{ -#if defined(XP_MAC) -#pragma unused (pool) -#endif - - return PR_MALLOC(size); -} - -static void PR_CALLBACK -NamedItemsFreeTable(void *pool, void *item) -{ -#if defined(XP_MAC) -#pragma unused (pool) -#endif - - PR_Free(item); -} - -static PLHashEntry * PR_CALLBACK -NamedItemsAllocEntry(void *pool, const void *key) -{ -#if defined(XP_MAC) -#pragma unused (pool,key) -#endif - - return PR_NEW(PLHashEntry); -} - -static void PR_CALLBACK -NamedItemsFreeEntry(void *pool, PLHashEntry *he, PRUintn flag) -{ -#if defined(XP_MAC) -#pragma unused (pool) -#endif - - if (flag == HT_FREE_ENTRY) { - Recycle((char *)he->key); - PR_Free(he); - } -} - -static PLHashAllocOps namedItemsHashAllocOps = { - NamedItemsAllocTable, NamedItemsFreeTable, - NamedItemsAllocEntry, NamedItemsFreeEntry -}; - - // ================================================================== // = // ================================================================== @@ -258,7 +208,6 @@ nsHTMLDocument::nsHTMLDocument() mLinks = nsnull; mAnchors = nsnull; mLayers = nsnull; - mNamedItems = nsnull; mParser = nsnull; mDTDMode = eDTDMode_quirks; mCSSLoader = nsnull; @@ -279,13 +228,14 @@ nsHTMLDocument::nsHTMLDocument() } mDomainWasSet = PR_FALSE; // Bug 13871: Frameset spoofing + + PrePopulateHashTables(); } nsHTMLDocument::~nsHTMLDocument() { PRInt32 i; - DeleteNamedItems(); NS_IF_RELEASE(mImages); NS_IF_RELEASE(mApplets); NS_IF_RELEASE(mEmbeds); @@ -330,6 +280,8 @@ nsHTMLDocument::~nsHTMLDocument() { nsServiceManager::ReleaseService("@mozilla.org/rdf/rdf-service;1", gRDF); } + + InvalidateHashTables(); } NS_IMETHODIMP @@ -388,7 +340,9 @@ nsHTMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) PRInt32 i; - DeleteNamedItems(); + InvalidateHashTables(); + PrePopulateHashTables(); + NS_IF_RELEASE(mImages); NS_IF_RELEASE(mApplets); NS_IF_RELEASE(mEmbeds); @@ -485,23 +439,26 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand, aChannel, aLoadGroup, aContainer, aDocListener, aReset); - if (NS_FAILED(rv)) { return rv; } + if (NS_FAILED(rv)) { + return rv; + } nsAutoString charset; charset.AssignWithConversion("ISO-8859-1"); // fallback value in case webShell return error nsCharsetSource charsetSource = kCharsetFromWeakDocTypeDefault; nsCOMPtr aURL; rv = aChannel->GetURI(getter_AddRefs(aURL)); - if (NS_FAILED(rv)) return rv; - + if (NS_FAILED(rv)) { + return rv; + } + nsAutoString lastModified; nsCOMPtr httpChannel = do_QueryInterface(aChannel); PRBool bTryCache = PR_FALSE; PRUint32 cacheFlags = 0; - if (httpChannel) - { + if (httpChannel) { nsXPIDLCString lastModHeader; nsIAtom* lastModKey = NS_NewAtom("last-modified"); @@ -510,36 +467,40 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand, NS_RELEASE(lastModKey); if (NS_SUCCEEDED(rv)) { - lastModified.AssignWithConversion( NS_STATIC_CAST(const char*, lastModHeader) ); + lastModified.AssignWithConversion(NS_STATIC_CAST(const char*, + lastModHeader)); SetLastModified(lastModified); } nsXPIDLCString referrerHeader; nsAutoString referrer; // The misspelled key 'referer' is as per the HTTP spec - nsIAtom* referrerKey = NS_NewAtom("referer"); - - rv = httpChannel->GetRequestHeader(referrerKey, - getter_Copies(referrerHeader)); + nsCOMPtr referrerKey(dont_AddRef(NS_NewAtom("referer"))); + + rv = httpChannel->GetRequestHeader(referrerKey, + getter_Copies(referrerHeader)); - NS_RELEASE(referrerKey); if (NS_SUCCEEDED(rv)) { - referrer.AssignWithConversion( NS_STATIC_CAST(const char*, referrerHeader) ); + referrer.AssignWithConversion(NS_STATIC_CAST(const char*, + referrerHeader)); SetReferrer(referrer); } - if(kCharsetFromHTTPHeader > charsetSource) - { - nsIAtom* contentTypeKey = NS_NewAtom("content-type"); + if(kCharsetFromHTTPHeader > charsetSource) { + nsCOMPtr + contentTypeKey(dont_AddRef(NS_NewAtom("content-type"))); + nsXPIDLCString contenttypeheader; - rv = httpChannel->GetResponseHeader(contentTypeKey, getter_Copies(contenttypeheader)); - NS_RELEASE(contentTypeKey); + rv = httpChannel->GetResponseHeader(contentTypeKey, + getter_Copies(contenttypeheader)); + if (NS_SUCCEEDED(rv)) { nsAutoString contentType; - contentType.AssignWithConversion( NS_STATIC_CAST(const char*, contenttypeheader) ); + contentType.AssignWithConversion(NS_STATIC_CAST(const char*, + contenttypeheader)); + PRInt32 start = contentType.RFind("charset=", PR_TRUE ) ; - if(kNotFound != start) - { + if(start != kNotFound) { start += 8; // 8 = "charset=".length PRInt32 end = 0; if(PRUnichar('"') == contentType.CharAt(start)) { @@ -554,17 +515,14 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand, } nsAutoString theCharset; contentType.Mid(theCharset, start, end - start); - nsICharsetAlias* calias = nsnull; - rv = nsServiceManager::GetService( - kCharsetAliasCID, - NS_GET_IID(nsICharsetAlias), - (nsISupports**) &calias); - if(NS_SUCCEEDED(rv) && (nsnull != calias) ) - { + nsCOMPtr calias(do_CreateInstance(kCharsetAliasCID, + &rv)); + + if(calias) { nsAutoString preferred; rv = calias->GetPreferred(theCharset, preferred); - if(NS_SUCCEEDED(rv)) - { + + if(NS_SUCCEEDED(rv)) { #ifdef DEBUG_charset char* cCharset = charset.ToNewCString(); printf("From HTTP Header, charset = %s\n", cCharset); @@ -573,7 +531,6 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand, charset = preferred; charsetSource = kCharsetFromHTTPHeader; } - nsServiceManager::ReleaseService(kCharsetAliasCID, calias); } } } @@ -582,14 +539,13 @@ nsHTMLDocument::StartDocumentLoad(const char* aCommand, PRUint32 loadAttr = 0; rv = httpChannel->GetLoadAttributes(&loadAttr); NS_ASSERTION(NS_SUCCEEDED(rv),"cannot get load attribute"); - if(NS_SUCCEEDED(rv) ) - { - // copy from nsHTTPChannel.cpp - if(loadAttr & nsIChannel::CACHE_AS_FILE) - cacheFlags = nsINetDataCacheManager::CACHE_AS_FILE; - else if(loadAttr & nsIChannel::INHIBIT_PERSISTENT_CACHING) - cacheFlags = nsINetDataCacheManager::BYPASS_PERSISTENT_CACHE; - bTryCache = PR_TRUE; + if(NS_SUCCEEDED(rv)) { + // copy from nsHTTPChannel.cpp + if(loadAttr & nsIChannel::CACHE_AS_FILE) + cacheFlags = nsINetDataCacheManager::CACHE_AS_FILE; + else if(loadAttr & nsIChannel::INHIBIT_PERSISTENT_CACHING) + cacheFlags = nsINetDataCacheManager::BYPASS_PERSISTENT_CACHE; + bTryCache = PR_TRUE; } // Don't propogate the result code beyond here, since it @@ -1292,55 +1248,47 @@ nsHTMLDocument::SetHeaderData(nsIAtom* aHeaderField, return result; } - NS_IMETHODIMP nsHTMLDocument::ContentAppended(nsIContent* aContainer, PRInt32 aNewIndexInContainer) { - if (nsnull != mNamedItems) { - nsCOMPtr name; + nsresult rv = RegisterNamedItems(aContainer); - aContainer->GetTag(*getter_AddRefs(name)); - RegisterNamedItems(aContainer, name.get() == nsHTMLAtoms::form); + if (NS_FAILED(rv)) { + return rv; } return nsDocument::ContentAppended(aContainer, aNewIndexInContainer); } NS_IMETHODIMP -nsHTMLDocument::ContentInserted(nsIContent* aContainer, - nsIContent* aChild, +nsHTMLDocument::ContentInserted(nsIContent* aContainer, nsIContent* aContent, PRInt32 aIndexInContainer) { - if (nsnull != mNamedItems) { - PRBool inform = PR_FALSE; - if (aContainer) { - nsIAtom *name; - aContainer->GetTag(name); - inform = (name == nsHTMLAtoms::form); - NS_IF_RELEASE(name); - } + nsresult rv = RegisterNamedItems(aContent); - RegisterNamedItems(aChild, inform); + if (NS_FAILED(rv)) { + return rv; } - return nsDocument::ContentInserted(aContainer, aChild, aIndexInContainer); + return nsDocument::ContentInserted(aContainer, aContent, aIndexInContainer); } NS_IMETHODIMP -nsHTMLDocument::ContentReplaced(nsIContent* aContainer, - nsIContent* aOldChild, +nsHTMLDocument::ContentReplaced(nsIContent* aContainer, nsIContent* aOldChild, nsIContent* aNewChild, PRInt32 aIndexInContainer) { - if (nsnull != mNamedItems) { - nsIAtom *name; + nsresult rv = UnregisterNamedItems(aOldChild); - aContainer->GetTag(name); - UnregisterNamedItems(aOldChild, name == nsHTMLAtoms::form); - RegisterNamedItems(aNewChild, name == nsHTMLAtoms::form); + if (NS_FAILED(rv)) { + return rv; + } - NS_IF_RELEASE(name); + rv = RegisterNamedItems(aNewChild); + + if (NS_FAILED(rv)) { + return rv; } return nsDocument::ContentReplaced(aContainer, aOldChild, @@ -1348,20 +1296,83 @@ nsHTMLDocument::ContentReplaced(nsIContent* aContainer, } NS_IMETHODIMP -nsHTMLDocument::ContentRemoved(nsIContent* aContainer, - nsIContent* aChild, +nsHTMLDocument::ContentRemoved(nsIContent* aContainer, nsIContent* aContent, PRInt32 aIndexInContainer) { - if ((nsnull != mNamedItems) && (nsnull != aContainer)) { - nsIAtom *name; + nsresult rv = UnregisterNamedItems(aContent); - aContainer->GetTag(name); - UnregisterNamedItems(aChild, name == nsHTMLAtoms::form); - - NS_IF_RELEASE(name); + if (NS_FAILED(rv)) { + return rv; } - return nsDocument::ContentRemoved(aContainer, aChild, aIndexInContainer); + return nsDocument::ContentRemoved(aContainer, aContent, aIndexInContainer); +} + +NS_IMETHODIMP +nsHTMLDocument::AttributeWillChange(nsIContent* aContent, PRInt32 aNameSpaceID, + nsIAtom* aAttribute) +{ + // XXX: Check namespaces!!! + + if (aAttribute == nsHTMLAtoms::name) { + nsCOMPtr tag; + nsAutoString value; + + aContent->GetTag(*getter_AddRefs(tag)); + + if (IsNamedItem(aContent, tag, value)) { + nsresult rv = RemoveFromNameTable(value, aContent); + + if (NS_FAILED(rv)) { + return rv; + } + } + } else if (aAttribute == nsHTMLAtoms::id) { + nsresult rv = RemoveFromIdTable(aContent); + + if (NS_FAILED(rv)) { + return rv; + } + } + + return nsDocument::AttributeWillChange(aContent, aNameSpaceID, aAttribute); +} + +NS_IMETHODIMP +nsHTMLDocument::AttributeChanged(nsIContent* aContent, PRInt32 aNameSpaceID, + nsIAtom* aAttribute, PRInt32 aHint) +{ + // XXX: Check namespaces! + + if (aAttribute == nsHTMLAtoms::name) { + nsCOMPtr tag; + nsAutoString value; + + aContent->GetTag(*getter_AddRefs(tag)); + + if (IsNamedItem(aContent, tag, value)) { + nsresult rv = AddToNameTable(value, aContent); + + if (NS_FAILED(rv)) { + return rv; + } + } + } else if (aAttribute == nsHTMLAtoms::id) { + nsAutoString value; + + aContent->GetAttribute(aNameSpaceID, nsHTMLAtoms::id, value); + + if (!value.IsEmpty()) { + nsresult rv = AddToIdTable(value, aContent, PR_TRUE); + + if (NS_FAILED(rv)) { + return rv; + } + } + } + + return nsDocument::AttributeChanged(aContent, aNameSpaceID, aAttribute, + aHint); } NS_IMETHODIMP @@ -1389,6 +1400,7 @@ nsHTMLDocument::FlushPendingNotifications(PRBool aFlushReflows) result = sink->FlushPendingNotifications(); } } + if (NS_SUCCEEDED(result)) { result = nsDocument::FlushPendingNotifications(aFlushReflows); } @@ -1984,7 +1996,7 @@ NS_IMETHODIMP nsHTMLDocument::GetLinks(nsIDOMHTMLCollection** aLinks) { if (nsnull == mLinks) { - mLinks = new nsContentList(this, MatchLinks, nsnull); + mLinks = new nsContentList(this, MatchLinks, nsString()); if (nsnull == mLinks) { return NS_ERROR_OUT_OF_MEMORY; } @@ -2018,16 +2030,15 @@ nsHTMLDocument::MatchAnchors(nsIContent *aContent, nsString* aData) NS_IMETHODIMP nsHTMLDocument::GetAnchors(nsIDOMHTMLCollection** aAnchors) { - if (nsnull == mAnchors) { - mAnchors = new nsContentList(this, MatchAnchors, nsnull); - if (nsnull == mAnchors) { - return NS_ERROR_OUT_OF_MEMORY; - } + if (!mAnchors) { + mAnchors = new nsContentList(this, MatchAnchors, nsString()); + NS_ENSURE_TRUE(mAnchors, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(mAnchors); } *aAnchors = (nsIDOMHTMLCollection *)mAnchors; - NS_ADDREF(mAnchors); + NS_ADDREF(*aAnchors); return NS_OK; } @@ -2126,48 +2137,55 @@ nsHTMLDocument::OpenCommon(nsIURI* aSourceURL) if (NS_FAILED(result)) return result; //Before we reset the doc notify the globalwindow of the change. + if (mScriptGlobalObject) { //Hold onto ourselves on the offchance that we're down to one ref - nsCOMPtr kungFuDeathGrip (do_QueryInterface((nsIHTMLDocument*)this)); + + nsCOMPtr kungFuDeathGrip = + do_QueryInterface((nsIHTMLDocument*)this); + result = mScriptGlobalObject->SetNewDocument(kungFuDeathGrip); - if (NS_FAILED(result)) return result; + + if (NS_FAILED(result)) + return result; } result = Reset(channel, group); - if (NS_FAILED(result)) return result; - if (NS_OK == result) { - result = nsComponentManager::CreateInstance(kCParserCID, nsnull, - NS_GET_IID(nsIParser), - (void **)&mParser); - mIsWriting = 1; - - if (NS_OK == result) { - nsCOMPtr sink; - nsCOMPtr webShell; - - // Get the webshell of our primary presentation shell - nsIPresShell* shell = (nsIPresShell*) mPresShells.ElementAt(0); - if (shell) { - nsCOMPtr cx; - shell->GetPresContext(getter_AddRefs(cx)); - nsCOMPtr container; - if (NS_OK == cx->GetContainer(getter_AddRefs(container))) { - if (container) { - webShell = do_QueryInterface(container); - } + if (NS_FAILED(result)) + return result; + + result = nsComponentManager::CreateInstance(kCParserCID, nsnull, + NS_GET_IID(nsIParser), + (void **)&mParser); + mIsWriting = 1; + + if (NS_SUCCEEDED(result)) { + nsCOMPtr sink; + nsCOMPtr webShell; + + // Get the webshell of our primary presentation shell + nsIPresShell* shell = (nsIPresShell*) mPresShells.ElementAt(0); + if (shell) { + nsCOMPtr cx; + shell->GetPresContext(getter_AddRefs(cx)); + nsCOMPtr container; + if (NS_OK == cx->GetContainer(getter_AddRefs(container))) { + if (container) { + webShell = do_QueryInterface(container); } } - - result = NS_NewHTMLContentSink(getter_AddRefs(sink), this, aSourceURL, webShell,channel); - - if (NS_OK == result) { - static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID); - nsCOMPtr theDTD(do_CreateInstance(kNavDTDCID, &result)); - if(NS_SUCCEEDED(result)) { - mParser->RegisterDTD(theDTD); - } - mParser->SetContentSink(sink); + } + + result = NS_NewHTMLContentSink(getter_AddRefs(sink), this, aSourceURL, + webShell, channel); + + if (NS_OK == result) { + static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID); + nsCOMPtr theDTD(do_CreateInstance(kNavDTDCID, &result)); + if(NS_SUCCEEDED(result)) { + mParser->RegisterDTD(theDTD); } + mParser->SetContentSink(sink); } } @@ -2231,11 +2249,12 @@ nsHTMLDocument::Close() { nsresult result = NS_OK; - if ((nsnull != mParser) && mIsWriting) { + if (mParser && mIsWriting) { nsAutoString emptyStr; emptyStr.AssignWithConversion(""); mWriteLevel++; result = mParser->Parse(emptyStr, NS_GENERATE_PARSER_KEY(), - NS_ConvertASCIItoUCS2("text/html"), PR_FALSE, PR_TRUE); + NS_ConvertASCIItoUCS2("text/html"), PR_FALSE, + PR_TRUE); mWriteLevel--; mIsWriting = 0; NS_IF_RELEASE(mParser); @@ -2252,7 +2271,7 @@ nsHTMLDocument::WriteCommon(const nsAReadableString& aText, if (nsnull == mParser) { result = Open(); - if (NS_OK != result) { + if (NS_FAILED(result)) { return result; } } @@ -2383,14 +2402,17 @@ nsIContent * nsHTMLDocument::MatchId(nsIContent *aContent, const nsAReadableString& aId) { nsAutoString value; - nsIContent *result = nsnull; - if ((NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::id, value)) && - aId.Equals(value)) { + nsresult rv = aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::id, + value); + + if (rv == NS_CONTENT_ATTR_HAS_VALUE && aId.Equals(value)) { return aContent; } + nsIContent *result = nsnull; PRInt32 i, count; + aContent->ChildCount(count); for (i = 0; i < count && result == nsnull; i++) { nsIContent *child; @@ -2403,25 +2425,41 @@ nsHTMLDocument::MatchId(nsIContent *aContent, const nsAReadableString& aId) } NS_IMETHODIMP -nsHTMLDocument::GetElementById(const nsAReadableString& aElementId, nsIDOMElement** aReturn) +nsHTMLDocument::GetElementById(const nsAReadableString& aElementId, + nsIDOMElement** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; - NS_WARN_IF_FALSE(!aElementId.IsEmpty(),"getElementById(\"\"), fix caller?"); + NS_WARN_IF_FALSE(!aElementId.IsEmpty(), "getElementById(\"\"), fix caller?"); if (aElementId.IsEmpty()) return NS_OK; - // XXX For now, we do a brute force search of the content tree. - // We should come up with a more efficient solution. - nsCOMPtr content = do_QueryInterface(MatchId(mRootContent,aElementId)); + nsStringKey key(aElementId); - nsresult rv = NS_OK; - if (content) { - rv = content->QueryInterface(NS_GET_IID(nsIDOMElement),(void**)aReturn); + 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 + + return NS_OK; + } else if (!e) { + 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); + + return NS_OK; + } + + // We found an element with a matching id, store that in the hash + mIdHashTable.Put(&key, e); } - return rv; + return CallQueryInterface(e, aReturn); } NS_IMETHODIMP @@ -2455,13 +2493,12 @@ nsHTMLDocument::GetElementsByTagNameNS(const nsAReadableString& aNamespaceURI, PRBool nsHTMLDocument::MatchNameAttribute(nsIContent* aContent, nsString* aData) { - nsresult result; nsAutoString name; - result = aContent->GetAttribute(kNameSpaceID_None, - nsHTMLAtoms::name, - name); - if ((result == NS_OK) && (nsnull != aData) && name.Equals(*aData)) { + nsresult rv = aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, + name); + + if (NS_SUCCEEDED(rv) && aData && name.Equals(*aData)) { return PR_TRUE; } else { @@ -2473,16 +2510,14 @@ NS_IMETHODIMP nsHTMLDocument::GetElementsByName(const nsAReadableString& aElementName, nsIDOMNodeList** aReturn) { - nsAutoString name(aElementName); + nsContentList* elements = new nsContentList(this, MatchNameAttribute, + aElementName); + NS_ENSURE_TRUE(elements, NS_ERROR_OUT_OF_MEMORY); - nsContentList* elements = new nsContentList(this, - MatchNameAttribute, - &name); - if (nsnull == elements) { - return NS_ERROR_OUT_OF_MEMORY; - } + *aReturn = elements; + NS_ADDREF(*aReturn); - return elements->QueryInterface(NS_GET_IID(nsIDOMNodeList), (void**)aReturn); + return NS_OK; } nsresult @@ -2519,9 +2554,12 @@ nsHTMLDocument::GetPixelDimensions(nsIPresShell* aShell, // If we have a view check if it's scrollable. If not, // just use the view size itself if (view) { - nsIScrollableView* scrollableView; - - if (NS_SUCCEEDED(view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollableView))) { + nsIScrollableView* scrollableView = nsnull; + + view->QueryInterface(NS_GET_IID(nsIScrollableView), + (void**)&scrollableView); + + if (scrollableView) { scrollableView->GetScrolledView(view); } @@ -2840,11 +2878,11 @@ nsHTMLDocument::GetLastModified(nsAWritableString& aLastModified) NS_IMETHODIMP nsHTMLDocument::GetEmbeds(nsIDOMHTMLCollection** aEmbeds) { - if (nsnull == mEmbeds) { - mEmbeds = new nsContentList(this, nsHTMLAtoms::embed, kNameSpaceID_Unknown); - if (nsnull == mEmbeds) { - return NS_ERROR_OUT_OF_MEMORY; - } + if (!mEmbeds) { + mEmbeds = new nsContentList(this, nsHTMLAtoms::embed, + kNameSpaceID_Unknown); + NS_ENSURE_TRUE(mEmbeds, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(mEmbeds); } @@ -2854,43 +2892,6 @@ nsHTMLDocument::GetEmbeds(nsIDOMHTMLCollection** aEmbeds) return NS_OK; } -#ifdef NS_IMPLEMENT_DOCUMENT_LAYERS -PRBool -nsHTMLDocument::MatchLayers(nsIContent *aContent, nsString* aData) -{ - nsIAtom *name; - aContent->GetTag(name); - nsAutoString attr; - PRBool result = PR_FALSE; - - if ((nsnull != name) && - ((nsHTMLAtoms::layer == name) || (nsHTMLAtoms::ilayer == name))) { - result = PR_TRUE; - } - - NS_IF_RELEASE(name); - return result; -} - -NS_IMETHODIMP -nsHTMLDocument::GetLayers(nsIDOMHTMLCollection** aLayers) -{ - if (nsnull == mLayers) { - mLayers = new nsContentList(this, MatchLayers, nsnull); - if (nsnull == mLayers) { - return NS_ERROR_OUT_OF_MEMORY; - } - NS_ADDREF(mLayers); - } - - *aLayers = (nsIDOMHTMLCollection *)mLayers; - NS_ADDREF(mLayers); - *aLayers = nsnull; - - return NS_OK; -} -#endif - NS_IMETHODIMP nsHTMLDocument::GetSelection(nsAWritableString& aReturn) { @@ -2971,212 +2972,508 @@ nsHTMLDocument::RouteEvent(nsIDOMEvent* aEvt) return NS_OK; } -void -nsHTMLDocument::DeleteNamedItems() +static PRBool PR_CALLBACK +NameHashCleanupEnumeratorCallback(nsHashKey *aKey, void *aData, void* closure) { - if (nsnull != mNamedItems) { - PL_HashTableDestroy(mNamedItems); - mNamedItems = nsnull; + nsBaseContentList *list = (nsBaseContentList *)aData; + + // The document this hash is in is most likely going away so we + // reset the live nodelists to avoid leaving dangling pointers to + // non-existing content + list->Reset(); + + NS_RELEASE(list); + + return PR_TRUE; +} + +void +nsHTMLDocument::InvalidateHashTables() +{ + mNameHashTable.Reset(NameHashCleanupEnumeratorCallback); + mIdHashTable.Reset(); +} + +static nsresult +AddEmptyListToHash(const nsAReadableString& aName, nsHashtable& aHash) +{ + nsBaseContentList *list = new nsBaseContentList(); + if (!list) { + return NS_ERROR_OUT_OF_MEMORY; } + + NS_ADDREF(list); + + nsStringKey key(aName); + + aHash.Put(&key, list); + + return NS_OK; +} + +// Pre-fill the name hash with names that are likely to be resolved in +// this document to avoid walking the tree looking for elements with +// these names. + +nsresult +nsHTMLDocument::PrePopulateHashTables() +{ + nsresult rv = NS_OK; + + rv = AddEmptyListToHash(NS_LITERAL_STRING("write"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("writeln"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("open"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("close"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("forms"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("elements"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("characterSet"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("nodeType"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("parentNode"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AddEmptyListToHash(NS_LITERAL_STRING("cookie"), mNameHashTable); + NS_ENSURE_SUCCESS(rv, rv); + + return rv; } static PRBool -IsNamedItem(nsIContent* aContent, nsIAtom *aTag, - PRBool aInForm, nsString& aName) +IsNamedItem(nsIContent* aContent, nsIAtom *aTag, nsAWritableString& aName) { // Only the content types reflected in Level 0 with a NAME // 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) || - (!aInForm && ((aTag == nsHTMLAtoms::applet) || - (aTag == nsHTMLAtoms::embed)))) { - if ((NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::name, aName)) || - (NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::id, aName))) { - return PR_TRUE; - } - } + (aTag == nsHTMLAtoms::applet) || (aTag == nsHTMLAtoms::embed) || + (aTag == nsHTMLAtoms::object)) { + aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, aName); -#ifdef NS_IMPLEMENT_DOCUMENT_LAYERS - if ((aTag == nsHTMLAtoms::layer) || (aTag == nsHTMLAtoms::ilayer)) { - if ((NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::name, aName)) || - (NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::id, aName))) { + if (!aName.IsEmpty()) { return PR_TRUE; } } -#endif return PR_FALSE; } -void -nsHTMLDocument::UnregisterNamedItems(nsIContent *aContent, PRBool aInForm) +nsresult +nsHTMLDocument::AddToNameTable(const nsAReadableString& aName, + nsIContent *aContent) { - nsAutoString value; - nsIAtom *tag; - aContent->GetTag(tag); - PRBool inForm; + nsStringKey key(aName); - if (IsNamedItem(aContent, tag, aInForm, value)) { - char *nameStr = value.ToNewCString(); - PL_HashTableRemove(mNamedItems, nameStr); - Recycle(nameStr); - } - - inForm = aInForm || (tag == nsHTMLAtoms::form); - NS_IF_RELEASE(tag); - - PRInt32 i, count; - aContent->ChildCount(count); - for (i = 0; i < count; i++) { - nsIContent *child; - aContent->ChildAt(i, child); - UnregisterNamedItems(child, inForm); - NS_RELEASE(child); - } -} + nsBaseContentList* list = NS_STATIC_CAST(nsBaseContentList *, + mNameHashTable.Get(&key)); -void -nsHTMLDocument::RegisterNamedItems(nsIContent *aContent, PRBool aInForm) -{ - nsAutoString value; - nsIAtom *tag; - aContent->GetTag(tag); - PRBool inForm; - - if (IsNamedItem(aContent, tag, aInForm, value)) { - char *nameStr = value.ToNewCString(); - // NSPR hashtables will leak keys if given the same entry twice, so - // remove first. If it's not in the table, this will fail silently - // and quickly (it's one extra lookup). - PL_HashTableRemove(mNamedItems, nameStr); - PL_HashTableAdd(mNamedItems, nameStr, aContent); + if (!list) { + return NS_OK; } - inForm = aInForm || (tag == nsHTMLAtoms::form); - NS_IF_RELEASE(tag); - - PRInt32 i, count; - aContent->ChildCount(count); - for (i = 0; i < count; i++) { - nsIContent *child; - aContent->ChildAt(i, child); - RegisterNamedItems(child, inForm); - NS_RELEASE(child); - } + PRInt32 i; + + list->IndexOf(aContent, i); + + if (i < 0) { + list->AppendElement(aContent); + } + + return NS_OK; } -nsIContent* -nsHTMLDocument::FindNamedItem(nsIContent *aContent, - const nsString& aName, - PRBool aInForm) +nsresult +nsHTMLDocument::AddToIdTable(const nsAReadableString& aId, + nsIContent *aContent, PRBool aPutInTable) +{ + nsStringKey key(aId); + + nsIContent *e = NS_STATIC_CAST(nsIContent *, mIdHashTable.Get(&key)); + + if (e == ELEMENT_NOT_IN_TABLE || (!e && aPutInTable)) { + mIdHashTable.Put(&key, aContent); + } + + return NS_OK; +} + +nsresult +nsHTMLDocument::RemoveFromNameTable(const nsAReadableString& aName, + nsIContent *aContent) +{ + nsStringKey key(aName); + + nsBaseContentList* list = NS_STATIC_CAST(nsBaseContentList *, + mNameHashTable.Get(&key)); + + if (list) { + list->RemoveElement(aContent); + } + + return NS_OK; +} + +nsresult +nsHTMLDocument::RemoveFromIdTable(nsIContent *aContent) { nsAutoString value; - nsIAtom *tag; - aContent->GetTag(tag); - PRBool inForm; - if (IsNamedItem(aContent, tag, aInForm, value)) { - if (aName.Equals(value)) { - NS_IF_RELEASE(tag); - return aContent; + aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, value); + + if (value.IsEmpty()) { + return NS_OK; + } + + nsStringKey key(value); + + nsIContent *e = NS_STATIC_CAST(nsIContent *, mIdHashTable.Get(&key)); + + if (e != aContent) + return NS_OK; + + mIdHashTable.Remove(&key); + + return NS_OK; +} + +nsresult +nsHTMLDocument::UnregisterNamedItems(nsIContent *aContent) +{ + nsCOMPtr tag; + + aContent->GetTag(*getter_AddRefs(tag)); + + if (tag == nsLayoutAtoms::textTagName) { + // Text nodes are not named items nor can they have children. + + return NS_OK; + } + + nsAutoString value; + nsresult rv = NS_OK; + + if (IsNamedItem(aContent, tag, value)) { + rv = RemoveFromNameTable(value, aContent); + + if (NS_FAILED(rv)) { + return rv; } } - inForm = aInForm || (tag == nsHTMLAtoms::form); - NS_IF_RELEASE(tag); - - PRInt32 i, count; - nsIContent *result = nsnull; - aContent->ChildCount(count); - for (i = 0; (i < count) && (nsnull == result); i++) { - nsIContent *child; - aContent->ChildAt(i, child); - result = FindNamedItem(child, aName, inForm); - NS_RELEASE(child); - } + rv = RemoveFromIdTable(aContent); - return result; + if (NS_FAILED(rv)) { + return rv; + } + + PRInt32 i, count; + + aContent->ChildCount(count); + + for (i = 0; i < count; i++) { + nsIContent *child; + + aContent->ChildAt(i, child); + + UnregisterNamedItems(child); + + NS_RELEASE(child); + } + + return NS_OK; +} + +nsresult +nsHTMLDocument::RegisterNamedItems(nsIContent *aContent) +{ + nsCOMPtr tag; + + aContent->GetTag(*getter_AddRefs(tag)); + + if (tag == nsLayoutAtoms::textTagName) { + // Text nodes are not named items nor can they have children. + + return NS_OK; + } + + nsAutoString value; + + if (IsNamedItem(aContent, tag, value)) { + AddToNameTable(value, aContent); + } + + aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, value); + + if (!value.IsEmpty()) { + nsresult rv = AddToIdTable(value, aContent, PR_FALSE); + + if (NS_FAILED(rv)) { + return rv; + } + } + + PRInt32 i, count; + + aContent->ChildCount(count); + + for (i = 0; i < count; i++) { + nsIContent *child; + + aContent->ChildAt(i, child); + + RegisterNamedItems(child); + + NS_RELEASE(child); + } + + return NS_OK; +} + +void +nsHTMLDocument::FindNamedItems(const nsAReadableString& aName, + nsIContent *aContent, nsBaseContentList& aList) +{ + nsCOMPtr tag; + nsAutoString value; + + aContent->GetTag(*getter_AddRefs(tag)); + + if (tag == nsLayoutAtoms::textTagName) { + // Text nodes are not named items nor can they have children. + + return; + } + + if (IsNamedItem(aContent, tag, value) && value.Equals(aName)) { + aList.AppendElement(aContent); + } else { + aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, value); + + if (value.Equals(aName)) { + AddToIdTable(value, aContent, PR_TRUE); + } + } + + PRInt32 i, count; + + aContent->ChildCount(count); + + nsCOMPtr child; + + for (i = 0; i < count; i++) { + aContent->ChildAt(i, *getter_AddRefs(child)); + + FindNamedItems(aName, child, aList); + } +} + +NS_IMETHODIMP +nsHTMLDocument::ResolveName(const nsAReadableString& aName, + nsIDOMHTMLFormElement *aForm, + nsISupports **aResult) +{ + *aResult = nsnull; + + // Bug 69826 - Make sure to flush the content model if the document + // is still loading. + + // 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)); + + if (!list) { +#ifdef DEBUG_jst + { + printf ("nsHTMLDocument name cache miss for name '%s'\n", + NS_ConvertUCS2toUTF8(aName).get()); + } +#endif + + list = new nsBaseContentList(); + NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); + + NS_ADDREF(list); + + FindNamedItems(aName, mRootContent, *list); + + 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 + + nsCOMPtr node; + + list->Item(0, getter_AddRefs(node)); + + if (aForm && node) { + // document.forms["foo"].bar should not map to
+ // 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 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 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. + + nsIContent *e = NS_STATIC_CAST(nsIContent *, mIdHashTable.Get(&key)); + + if (e) { + nsCOMPtr tag; + e->GetTag(*getter_AddRefs(tag)); + + if (tag.get() == nsHTMLAtoms::embed || + tag.get() == nsHTMLAtoms::img || + tag.get() == nsHTMLAtoms::object || + tag.get() == nsHTMLAtoms::applet) { + *aResult = e; + NS_ADDREF(*aResult); + } + } + + return NS_OK; } NS_IMETHODIMP nsHTMLDocument::NamedItem(JSContext* cx, jsval* argv, PRUint32 argc, jsval* aReturn) { - nsresult result = NS_OK; - nsIContent *content = nsnull; + nsresult rv = NS_OK; if (argc < 1) return NS_ERROR_DOM_TOO_FEW_PARAMETERS_ERR; - char *str = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - nsAutoString name; name.AssignWithConversion(str); + JSString *jsstr = ::JS_ValueToString(cx, argv[0]); + PRUnichar *ustr = NS_REINTERPRET_CAST(PRUnichar *, JS_GetStringChars(jsstr)); - if (!name.EqualsWithConversion("write") && !name.EqualsWithConversion("writeln") && - !name.EqualsWithConversion("close") && !name.EqualsWithConversion("open")) { - // XXX If we have a parser, it means that we're still loading the - // document. Since there's still content coming in (and not all - // may yet have been explicitly added to the document), we do - // a depth-first search rather than build up a table. - // Obviously, this may be inefficient for large documents. - if (nsnull != mParser) { - - // Bug 69826 - Make sure to flush the content model if - // the document is still loading. - FlushPendingNotifications(PR_FALSE); - - content = FindNamedItem(mRootContent, name, PR_FALSE); - } - else { - // If the document has completed loading, we build a table and - // cache the named items. The table will be updated as content - // is added and removed. - if (nsnull == mNamedItems) { - mNamedItems = PL_NewHashTable(10, PL_HashString, PL_CompareStrings, - PL_CompareValues, &namedItemsHashAllocOps, - nsnull); - RegisterNamedItems(mRootContent, PR_FALSE); - } + nsCOMPtr item; - content = (nsIContent *)PL_HashTableLookup(mNamedItems, str); - } - } + rv = ResolveName(nsLiteralString(ustr, ::JS_GetStringLength(jsstr)), + nsnull, getter_AddRefs(item)); - nsIScriptContext *context = (nsIScriptContext*)JS_GetContextPrivate(cx); + nsCOMPtr owner(do_QueryInterface(item)); + + nsIScriptContext *context = (nsIScriptContext*)::JS_GetContextPrivate(cx); JSObject *scriptObject; - result = GetScriptObject(context, (void **)&scriptObject); - if (NS_FAILED(result)) - return result; + rv = GetScriptObject(context, (void **)&scriptObject); + if (NS_FAILED(rv)) + return rv; - if (nsnull != content) { - nsIScriptSecurityManager *sm = nsJSUtils::nsGetSecurityManager(cx, scriptObject); - result = sm->CheckScriptAccess(cx, scriptObject, - NS_DOM_PROP_NSHTMLFORMELEMENT_NAMEDITEM, - PR_FALSE); - if (NS_SUCCEEDED(result)) { - nsCOMPtr owner = do_QueryInterface(content); + if (owner) { + nsIScriptSecurityManager *sm = + nsJSUtils::nsGetSecurityManager(cx, scriptObject); + + rv = sm->CheckScriptAccess(cx, scriptObject, + NS_DOM_PROP_NSHTMLFORMELEMENT_NAMEDITEM, + PR_FALSE); + if (NS_SUCCEEDED(rv)) { JSObject* obj; - - result = owner->GetScriptObject(context, (void**)&obj); - if (NS_FAILED(result)) { - return result; + + rv = owner->GetScriptObject(context, (void**)&obj); + if (NS_FAILED(rv)) { + return rv; } + *aReturn = OBJECT_TO_JSVAL(obj); } - return result; + + return rv; } - nsISupports *supports; - result = this->QueryInterface(NS_GET_IID(nsISupports), (void **) &supports); - if (NS_SUCCEEDED(result)) { - result = nsJSUtils::nsCallJSScriptObjectGetProperty(supports, cx, scriptObject, - argv[0], aReturn); - NS_RELEASE(supports); + + nsCOMPtr supports; + rv = this->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(supports)); + + if (NS_SUCCEEDED(rv)) { + rv = nsJSUtils::nsCallJSScriptObjectGetProperty(supports, cx, scriptObject, + argv[0], aReturn); } - return result; + + return rv; } NS_IMETHODIMP -nsHTMLDocument::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject) +nsHTMLDocument::GetScriptObject(nsIScriptContext *aContext, + void** aScriptObject) { nsresult res = NS_OK; nsCOMPtr global; @@ -3234,10 +3531,10 @@ nsHTMLDocument::Resolve(JSContext *aContext, JSObject *aObj, jsval aID, result = NamedItem(aContext, &aID, 1, &val); if (NS_SUCCEEDED(result) && val) { - char *str = JS_GetStringBytes(JSVAL_TO_STRING(aID)); - ret = ::JS_DefineProperty(aContext, aObj, - str, val, - nsnull, nsnull, 0); + JSString *str = JSVAL_TO_STRING(aID); + ret = ::JS_DefineUCProperty(aContext, aObj,JS_GetStringChars(str), + JS_GetStringLength(str), val, nsnull, + nsnull, 0); *aDidDefineProperty = PR_TRUE; } @@ -3330,11 +3627,10 @@ nsHTMLDocument::AddForm(nsIDOMHTMLFormElement *aForm) NS_IMETHODIMP nsHTMLDocument::GetForms(nsIDOMHTMLCollection** aForms) { - if (nsnull == mForms) { + if (!mForms) { mForms = new nsContentList(this, nsHTMLAtoms::form, kNameSpaceID_Unknown); - if (nsnull == mForms) { - return NS_ERROR_OUT_OF_MEMORY; - } + NS_ENSURE_TRUE(mForms, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(mForms); } diff --git a/mozilla/content/html/document/src/nsHTMLDocument.h b/mozilla/content/html/document/src/nsHTMLDocument.h index 032005b05bf..478296cd725 100644 --- a/mozilla/content/html/document/src/nsHTMLDocument.h +++ b/mozilla/content/html/document/src/nsHTMLDocument.h @@ -31,12 +31,13 @@ #include "nsIDOMNode.h" #include "nsIDOMHTMLBodyElement.h" #include "nsIHTMLContentContainer.h" -#include "plhash.h" +#include "nsHashtable.h" #include "jsapi.h" #include "rdf.h" #include "nsRDFCID.h" #include "nsIRDFService.h" +class nsBaseContentList; class nsContentList; class nsIHTMLStyleSheet; class nsIHTMLCSSStyleSheet; @@ -115,6 +116,14 @@ public: NS_IMETHOD ContentRemoved(nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer); + NS_IMETHOD AttributeChanged(nsIContent* aChild, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute, + PRInt32 aHint); + NS_IMETHOD AttributeWillChange(nsIContent* aChild, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute); + NS_IMETHOD FlushPendingNotifications(PRBool aFlushReflows = PR_TRUE); // nsIDOMDocument interface @@ -126,7 +135,9 @@ public: // nsIDOMHTMLDocument interface NS_DECL_IDOMHTMLDOCUMENT NS_DECL_IDOMNSHTMLDOCUMENT - // the following is not part of nsIDOMHTMLDOCUMENT but allows the content sink to add forms + + // the following is not part of nsIDOMHTMLDocument but allows the + // content sink to add forms NS_IMETHOD AddForm(nsIDOMHTMLFormElement* aForm); // From nsIScriptObjectOwner interface, implemented by nsDocument @@ -141,18 +152,31 @@ public: */ NS_IMETHOD WasDomainSet(PRBool* aDomainWasSet); + NS_IMETHOD ResolveName(const nsAReadableString& aName, + nsIDOMHTMLFormElement *aForm, + nsISupports **aResult); + protected: nsresult GetPixelDimensions(nsIPresShell* aShell, PRInt32* aWidth, PRInt32* aHeight); - void RegisterNamedItems(nsIContent *aContent, PRBool aInForm); - void UnregisterNamedItems(nsIContent *aContent, PRBool aInForm); - nsIContent* FindNamedItem(nsIContent *aContent, const nsString& aName, - PRBool aInForm); + 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 RemoveFromIdTable(nsIContent *aContent); + + void InvalidateHashTables(); + nsresult PrePopulateHashTables(); - void DeleteNamedItems(); 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, @@ -195,8 +219,6 @@ protected: nsContentList *mForms; nsContentList *mLayers; - PLHashTable *mNamedItems; - nsIParser *mParser; static nsrefcnt gRefCntRDFService; @@ -212,6 +234,8 @@ protected: */ PRBool mDomainWasSet; + nsHashtable mNameHashTable; + nsHashtable mIdHashTable; }; #endif /* nsHTMLDocument_h___ */ diff --git a/mozilla/content/html/document/src/nsIHTMLDocument.h b/mozilla/content/html/document/src/nsIHTMLDocument.h index 87000c467a1..cea7e37cae5 100644 --- a/mozilla/content/html/document/src/nsIHTMLDocument.h +++ b/mozilla/content/html/document/src/nsIHTMLDocument.h @@ -33,6 +33,7 @@ class nsIDOMHTMLMapElement; class nsIHTMLStyleSheet; class nsIStyleSheet; class nsICSSLoader; +class nsIContent; /* b2a848b0-d0a9-11d1-89b1-006008911b81 */ #define NS_IHTMLDOCUMENT_IID \ @@ -75,6 +76,10 @@ public: * Returns true if document.domain was set for this document */ NS_IMETHOD WasDomainSet(PRBool* aDomainWasSet) = 0; + + NS_IMETHOD ResolveName(const nsAReadableString& aName, + nsIDOMHTMLFormElement *aForm, + nsISupports **aResult) = 0; }; #endif /* nsIHTMLDocument_h___ */ diff --git a/mozilla/content/xul/content/src/nsXULElement.cpp b/mozilla/content/xul/content/src/nsXULElement.cpp index adf942422d9..0725ba86562 100644 --- a/mozilla/content/xul/content/src/nsXULElement.cpp +++ b/mozilla/content/xul/content/src/nsXULElement.cpp @@ -2922,6 +2922,10 @@ nsXULElement::SetAttribute(nsINodeInfo* aNodeInfo, aNodeInfo->GetNameAtom(*getter_AddRefs(attrName)); aNodeInfo->GetNamespaceID(attrns); + if (mDocument) { + mDocument->AttributeWillChange(this, attrns, attrName); + } + if (! Attributes()) { rv = EnsureSlots(); if (NS_FAILED(rv)) return rv; @@ -3096,7 +3100,8 @@ nsXULElement::SetAttribute(nsINodeInfo* aNodeInfo, (tagName.get() == nsXULAtoms::command) || (tagName.get() == nsXULAtoms::key)) return rv; - mDocument->AttributeChanged(NS_STATIC_CAST(nsIStyledContent*, this), attrns, attrName, NS_STYLE_HINT_UNKNOWN); + mDocument->AttributeChanged(this, attrns, attrName, + NS_STYLE_HINT_UNKNOWN); } } diff --git a/mozilla/content/xul/document/src/nsXULDocument.cpp b/mozilla/content/xul/document/src/nsXULDocument.cpp index 3e9b031e38f..27b4744e071 100644 --- a/mozilla/content/xul/document/src/nsXULDocument.cpp +++ b/mozilla/content/xul/document/src/nsXULDocument.cpp @@ -1788,6 +1788,14 @@ nsXULDocument::ContentRemoved(nsIContent* aContainer, return NS_OK; } +NS_IMETHODIMP +nsXULDocument::AttributeWillChange(nsIContent* aChild, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute) +{ + return NS_OK; +} + NS_IMETHODIMP nsXULDocument::StyleRuleChanged(nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule, diff --git a/mozilla/content/xul/document/src/nsXULDocument.h b/mozilla/content/xul/document/src/nsXULDocument.h index 0ba3f99af3c..9c172aa9d7f 100644 --- a/mozilla/content/xul/document/src/nsXULDocument.h +++ b/mozilla/content/xul/document/src/nsXULDocument.h @@ -246,6 +246,9 @@ public: NS_IMETHOD ContentRemoved(nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer); + NS_IMETHOD AttributeWillChange(nsIContent* aChild, + PRInt32 aNameSpaceID, + nsIAtom* aAttribute); NS_IMETHOD StyleRuleChanged(nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule,