From d37f165004d2f18476463572aa197e1a4fa6a823 Mon Sep 17 00:00:00 2001 From: "sicking%bigfoot.com" Date: Sun, 23 Dec 2001 16:06:13 +0000 Subject: [PATCH] Bug 17003. Give textareas a childnode and connect that childnode to .defaultValue. Also make textareas work properly for display: none; r=jkeiser sr=jst --- .../content/src/nsHTMLTextAreaElement.cpp | 182 +++++++++++++++--- .../html/document/src/nsHTMLContentSink.cpp | 70 ++++--- content/xml/document/src/nsXMLContentSink.cpp | 14 +- content/xml/document/src/nsXMLContentSink.h | 2 - 4 files changed, 191 insertions(+), 77 deletions(-) diff --git a/content/html/content/src/nsHTMLTextAreaElement.cpp b/content/html/content/src/nsHTMLTextAreaElement.cpp index 2c6653dcc1d..fa3a2632d3f 100644 --- a/content/html/content/src/nsHTMLTextAreaElement.cpp +++ b/content/html/content/src/nsHTMLTextAreaElement.cpp @@ -69,6 +69,9 @@ #include "nsGUIEvent.h" #include "nsLinebreakConverter.h" #include "nsIPresState.h" +#include "nsIDOMText.h" +#include "nsReadableUtils.h" +#include "nsITextContent.h" static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); @@ -117,6 +120,13 @@ public: NS_IMETHOD SetValueChanged(PRBool aValueChanged); // nsIContent + NS_IMETHOD InsertChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify, + PRBool aDeepSetDocument); + NS_IMETHOD ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify, + PRBool aDeepSetDocument); + NS_IMETHOD AppendChildTo(nsIContent* aKid, PRBool aNotify, + PRBool aDeepSetDocument); + NS_IMETHOD RemoveChildAt(PRInt32 aIndex, PRBool aNotify); NS_IMETHOD StringToAttribute(nsIAtom* aAttribute, const nsAReadableString& aValue, nsHTMLValue& aResult); @@ -134,6 +144,8 @@ public: protected: nsCOMPtr mControllers; + char* mValue; + PRPackedBool mValueChanged; NS_IMETHOD SelectAll(nsIPresContext* aPresContext); }; @@ -167,6 +179,8 @@ NS_NewHTMLTextAreaElement(nsIHTMLContent** aInstancePtrResult, nsHTMLTextAreaElement::nsHTMLTextAreaElement() { + mValue = 0; + mValueChanged = PR_FALSE; } nsHTMLTextAreaElement::~nsHTMLTextAreaElement() @@ -410,10 +424,13 @@ nsHTMLTextAreaElement::GetValue(nsAWritableString& aValue) formControlFrame->GetProperty(nsHTMLAtoms::value, aValue); return NS_OK; } else { - return nsGenericHTMLContainerFormElement::GetAttr(kNameSpaceID_HTML, - nsHTMLAtoms::value, - aValue); + if (!mValueChanged || !mValue) { + GetDefaultValue(aValue); + } else { + aValue = NS_ConvertUTF8toUCS2(mValue); + } } + return NS_OK; } @@ -443,14 +460,15 @@ nsHTMLTextAreaElement::SetValueGuaranteed(const nsAString& aValue, formControlFrame->SetProperty(presContext, nsHTMLAtoms::value, aValue); } + else { + if (mValue) { + nsMemory::Free(mValue); + } + mValue = ToNewUTF8String(aValue); + NS_ENSURE_TRUE(mValue, NS_ERROR_OUT_OF_MEMORY); - // Always set the value internally, since it affects layout - // - // Set the attribute in the DOM too, we call SetAttribute with aNotify - // false so that we don't generate unnecessary reflows. - nsGenericHTMLContainerFormElement::SetAttr(kNameSpaceID_HTML, - nsHTMLAtoms::value, aValue, - PR_FALSE); + SetValueChanged(PR_TRUE); + } return NS_OK; } @@ -465,41 +483,136 @@ nsHTMLTextAreaElement::SetValue(const nsAReadableString& aValue) NS_IMETHODIMP nsHTMLTextAreaElement::SetValueChanged(PRBool aValueChanged) { + mValueChanged = aValueChanged; + if (!aValueChanged && mValue) { + nsMemory::Free(mValue); + mValue = nsnull; + } return NS_OK; } NS_IMETHODIMP nsHTMLTextAreaElement::GetDefaultValue(nsAWritableString& aDefaultValue) { - nsGenericHTMLContainerFormElement::GetAttr(kNameSpaceID_HTML, - nsHTMLAtoms::defaultvalue, - aDefaultValue); + nsresult rv; + PRInt32 nChildren, i; - return NS_OK; + nsAutoString defVal; + + ChildCount(nChildren); + for (i = 0; i < nChildren; i++) { + nsCOMPtr child; + nsCOMPtr textNode; + + rv = ChildAt(i, *getter_AddRefs(child)); + NS_ENSURE_SUCCESS(rv, rv); + textNode = do_QueryInterface(child); + if(textNode) { + nsAutoString tmp; + textNode->GetData(tmp); + defVal.Append(tmp); + } + } + + aDefaultValue.Assign(defVal); + + return NS_OK; } NS_IMETHODIMP nsHTMLTextAreaElement::SetDefaultValue(const nsAReadableString& aDefaultValue) { - nsAutoString defaultValue(aDefaultValue); + nsresult rv; + PRInt32 nChildren, i; + PRBool firstChildUsed = PR_FALSE; - // normalize line breaks. Need this e.g. when the value is - // coming from a URL, which used platform line breaks. - nsLinebreakConverter::ConvertStringLineBreaks(defaultValue, - nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakContent); + ChildCount(nChildren); + // If a child exist we try to reuse it + if (nChildren > 0) { + nsCOMPtr child; + nsCOMPtr textNode; - // Strip only one leading LF if there is one (bug 40394) - if (0 == defaultValue.Find("\n", PR_FALSE, 0, 1)) { - defaultValue.Cut(0,1); + rv = ChildAt(0, *getter_AddRefs(child)); + NS_ENSURE_SUCCESS(rv, rv); + textNode = do_QueryInterface(child); + if(textNode) { + rv = textNode->SetData(aDefaultValue); + NS_ENSURE_SUCCESS(rv, rv); + firstChildUsed = PR_TRUE; + } } - - nsGenericHTMLContainerFormElement::SetAttr(kNameSpaceID_HTML, - nsHTMLAtoms::defaultvalue, - defaultValue, PR_TRUE); - SetValue(defaultValue); + + PRInt32 lastChild = firstChildUsed ? 1 : 0; + for (i = nChildren-1; i >= lastChild; i--) { + RemoveChildAt(i, PR_TRUE); + } + + if (!firstChildUsed) { + nsCOMPtr textContent; + rv = NS_NewTextNode(getter_AddRefs(textContent)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr textNode; + textNode = do_QueryInterface(textContent); + rv = textNode->SetData(aDefaultValue); + NS_ENSURE_SUCCESS(rv, rv); + + AppendChildTo(textContent, PR_TRUE, PR_TRUE); + } + return NS_OK; } +NS_IMETHODIMP +nsHTMLTextAreaElement::InsertChildAt(nsIContent* aKid, PRInt32 aIndex, + PRBool aNotify, PRBool aDeepSetDocument) +{ + nsresult rv; + rv = nsGenericHTMLContainerFormElement::InsertChildAt(aKid, aIndex, aNotify, + aDeepSetDocument); + if (!mValueChanged) { + Reset(); + } + return rv; +} + +NS_IMETHODIMP +nsHTMLTextAreaElement::ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex, + PRBool aNotify, PRBool aDeepSetDocument) +{ + nsresult rv; + rv = nsGenericHTMLContainerFormElement::ReplaceChildAt(aKid, aIndex, aNotify, + aDeepSetDocument); + if (!mValueChanged) { + Reset(); + } + return rv; +} + +NS_IMETHODIMP +nsHTMLTextAreaElement::AppendChildTo(nsIContent* aKid, PRBool aNotify, + PRBool aDeepSetDocument) +{ + nsresult rv; + rv = nsGenericHTMLContainerFormElement::AppendChildTo(aKid, aNotify, + aDeepSetDocument); + if (!mValueChanged) { + Reset(); + } + return rv; +} + +NS_IMETHODIMP +nsHTMLTextAreaElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) +{ + nsresult rv; + rv = nsGenericHTMLContainerFormElement::RemoveChildAt(aIndex, aNotify); + if (!mValueChanged) { + Reset(); + } + return rv; +} + NS_IMETHODIMP nsHTMLTextAreaElement::StringToAttribute(nsIAtom* aAttribute, const nsAReadableString& aValue, @@ -747,10 +860,17 @@ nsHTMLTextAreaElement::GetControllers(nsIControllers** aResult) nsresult nsHTMLTextAreaElement::Reset() { - nsAutoString resetVal; - GetDefaultValue(resetVal); - nsresult rv = SetValue(resetVal); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv; + // If the frame is there, we have to set the value so that it will show up. + nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE); + if (formControlFrame) { + nsAutoString resetVal; + GetDefaultValue(resetVal); + rv = SetValue(resetVal); + NS_ENSURE_SUCCESS(rv, rv); + formControlFrame->OnContentReset(); + } + SetValueChanged(PR_FALSE); return NS_OK; } diff --git a/content/html/document/src/nsHTMLContentSink.cpp b/content/html/document/src/nsHTMLContentSink.cpp index d106d862ef9..7045fdde8a8 100644 --- a/content/html/document/src/nsHTMLContentSink.cpp +++ b/content/html/document/src/nsHTMLContentSink.cpp @@ -803,7 +803,7 @@ MakeContentObject(nsHTMLTag aNodeType, nsIDOMHTMLFormElement* aForm, nsIWebShell* aWebShell, nsIHTMLContent** aResult, - const nsString* aContent = nsnull, + const nsAString* aSkippedContent = nsnull, PRBool aInsideNoXXXTag = PR_FALSE); @@ -843,14 +843,15 @@ HTMLContentSink::CreateContentObject(const nsIParserNode& aNode, } if (NS_SUCCEEDED(rv)) { - // Make the content object - // XXX why is textarea not a container? - nsAutoString content; - if (eHTMLTag_textarea == aNodeType) { - content.Assign(aNode.GetSkippedContent()); + // XXX if the parser treated the text in a textarea like a normal textnode + // we wouldn't need to do this. + const nsAString* skippedContent = nsnull; + if (aNodeType == eHTMLTag_textarea) { + skippedContent = &aNode.GetSkippedContent(); } + // Make the content object rv = MakeContentObject(aNodeType, nodeInfo, aForm, aWebShell, - aResult, &content, !!mInsideNoXXXTag); + aResult, skippedContent, !!mInsideNoXXXTag); PRInt32 id; mDocument->GetAndIncrementContentID(&id); @@ -991,7 +992,7 @@ MakeContentObject(nsHTMLTag aNodeType, nsIDOMHTMLFormElement* aForm, nsIWebShell* aWebShell, nsIHTMLContent** aResult, - const nsString* aContent, + const nsAString* aSkippedContent, PRBool aInsideNoXXXTag) { nsresult rv = NS_OK; @@ -1218,23 +1219,32 @@ MakeContentObject(nsHTMLTag aNodeType, rv = NS_NewHTMLTableCellElement(aResult, aNodeInfo); break; case eHTMLTag_textarea: - //if (nsHTMLElementFactory::mUseXBLForms) - // rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); - //else { - rv = NS_NewHTMLTextAreaElement(aResult, aNodeInfo); - // XXX why is textarea not a container. If it were, this code would not be necessary - // If the text area has some content, set it - if (aContent && (aContent->Length() > 0)) { - nsIDOMHTMLTextAreaElement* taElem; - rv = (*aResult)->QueryInterface(NS_GET_IID(nsIDOMHTMLTextAreaElement), (void **)&taElem); - if ((NS_OK == rv) && taElem) { - taElem->SetDefaultValue(*aContent); - NS_RELEASE(taElem); + rv = NS_NewHTMLTextAreaElement(aResult, aNodeInfo); + // XXX if the parser treated the text in a textarea like a normal textnode + // we wouldn't need to do this. + // If the text area has some content, set it + if (aSkippedContent && (!aSkippedContent->IsEmpty())) { + // Strip only one leading newline if there is one (bug 40394) + nsString::const_iterator start, end; + aSkippedContent->BeginReading(start); + aSkippedContent->EndReading(end); + if (*start == nsCRT::CR) { + ++start; + if (start != end && *start == nsCRT::LF) { + ++start; } } - if (!aInsideNoXXXTag) - SetForm(*aResult, aForm); - //} + else if (*start == nsCRT::LF) { + ++start; + } + + nsCOMPtr ta(do_QueryInterface(*aResult)); + if (ta) { + ta->SetDefaultValue(Substring(start, end)); + } + } + if (!aInsideNoXXXTag) + SetForm(*aResult, aForm); break; case eHTMLTag_title: rv = NS_NewHTMLTitleElement(aResult, aNodeInfo); @@ -2224,14 +2234,12 @@ SinkContext::FlushText(PRBool* aDidFlush, PRBool aReleaseLast) FlushText(aDidFlush, aReleaseLast); } else { - nsCOMPtr cdata = do_QueryInterface(mLastTextNode, &rv); - if (NS_SUCCEEDED(rv)) { - CBufDescriptor bd(mText, PR_TRUE, mTextSize+1, mTextLength); - bd.mIsConst = PR_TRUE; - nsAutoString str(bd); - - rv = cdata->AppendData(str); - + + nsCOMPtr cdata(do_QueryInterface(mLastTextNode)); + + if (cdata) { + rv = cdata->AppendData(Substring(mText, mText + mTextLength)); + mLastTextNodeSize += mTextLength; mTextLength = 0; didFlush = PR_TRUE; diff --git a/content/xml/document/src/nsXMLContentSink.cpp b/content/xml/document/src/nsXMLContentSink.cpp index cd3319cf5c1..18271c5338f 100644 --- a/content/xml/document/src/nsXMLContentSink.cpp +++ b/content/xml/document/src/nsXMLContentSink.cpp @@ -730,9 +730,7 @@ nsXMLContentSink::OpenContainer(const nsIParserNode& aNode) result = NS_CreateHTMLElement(getter_AddRefs(htmlContent), nodeInfo, PR_TRUE); content = do_QueryInterface(htmlContent); - if (tagAtom.get() == nsHTMLAtoms::textarea) { - mTextAreaElement = do_QueryInterface(htmlContent); - } else if (tagAtom.get() == nsHTMLAtoms::style) { + if (tagAtom.get() == nsHTMLAtoms::style) { mStyleElement = htmlContent; } else if (tagAtom.get() == nsHTMLAtoms::base) { if (!mBaseElement) { @@ -861,12 +859,6 @@ nsXMLContentSink::CloseContainer(const nsIParserNode& aNode) } mInTitle = PR_FALSE; } - } else if (tagAtom.get() == nsHTMLAtoms::textarea) { - if (mTextAreaElement) { - mTextAreaElement->SetDefaultValue(mTextareaText); - mTextAreaElement = nsnull; - mTextareaText.Truncate(); - } } else if (tagAtom.get() == nsHTMLAtoms::style) { if (mStyleElement) { result = ProcessSTYLETag(aNode); @@ -1019,8 +1011,6 @@ nsXMLContentSink::AddCDATASection(const nsIParserNode& aNode) const nsAReadableString& text = aNode.GetText(); if (mInTitle) { mTitleText.Append(text); - } else if (mTextAreaElement) { - mTextareaText.Append(text); } else if (mStyleElement) { mStyleText.Append(text); } @@ -1505,8 +1495,6 @@ nsXMLContentSink::AddText(const nsAReadableString& aString) if (mInTitle) { mTitleText.Append(aString); - } else if (mTextAreaElement) { - mTextareaText.Append(aString); } else if (mStyleElement) { mStyleText.Append(aString); } diff --git a/content/xml/document/src/nsXMLContentSink.h b/content/xml/document/src/nsXMLContentSink.h index ee9f018874d..9f21f1d15e9 100644 --- a/content/xml/document/src/nsXMLContentSink.h +++ b/content/xml/document/src/nsXMLContentSink.h @@ -217,9 +217,7 @@ protected: nsString mRef; // ScrollTo #ref nsString mTitleText; - nsString mTextareaText; - nsCOMPtr mTextAreaElement; nsCOMPtr mStyleElement; nsCOMPtr mBaseElement; nsCOMPtr mMetaElement;