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,