From ea199784e9646cf29089a13a7c47d2cc993cdbcd Mon Sep 17 00:00:00 2001 From: "cvshook%sicking.cc" Date: Tue, 25 Apr 2006 09:18:42 +0000 Subject: [PATCH] Bug 334977. Separate mFileName from mValue. r/sr=bz git-svn-id: svn://10.0.0.236/trunk@195344 18797224-902f-48f8-a5cc-f745e15eee43 --- .../content/html/content/public/Makefile.in | 1 + .../content/public/nsIFileControlElement.h | 73 ++++++++ .../html/content/src/nsHTMLInputElement.cpp | 168 +++++++++++++----- mozilla/layout/forms/nsFileControlFrame.cpp | 69 ++----- mozilla/layout/forms/nsFileControlFrame.h | 10 -- 5 files changed, 213 insertions(+), 108 deletions(-) create mode 100755 mozilla/content/html/content/public/nsIFileControlElement.h diff --git a/mozilla/content/html/content/public/Makefile.in b/mozilla/content/html/content/public/Makefile.in index 72e95bd808b..a6623baa95c 100644 --- a/mozilla/content/html/content/public/Makefile.in +++ b/mozilla/content/html/content/public/Makefile.in @@ -61,6 +61,7 @@ EXPORTS = \ nsIRadioVisitor.h \ nsIRadioGroupContainer.h \ nsITextControlElement.h \ + nsIFileControlElement.h \ nsIFormSubmission.h \ nsIFrameSetElement.h \ $(NULL) diff --git a/mozilla/content/html/content/public/nsIFileControlElement.h b/mozilla/content/html/content/public/nsIFileControlElement.h new file mode 100755 index 00000000000..ad18f362841 --- /dev/null +++ b/mozilla/content/html/content/public/nsIFileControlElement.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jonas Sicking (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsIFileControlElement_h___ +#define nsIFileControlElement_h___ + +#include "nsISupports.h" +class nsAString; + +// IID for the nsIFileControl interface +#define NS_IFILECONTROLELEMENT_IID \ +{ 0x1f6a32fd, 0x9cda, 0x43e9, \ + { 0x90, 0xef, 0x18, 0x0a, 0xd5, 0xe6, 0xcd, 0xa9 } } + +/** + * This interface is used for the file control frame to store its value away + * into the content. + */ +class nsIFileControlElement : public nsISupports { +public: + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFILECONTROLELEMENT_IID) + + /** + * Gets filename to be uploaded when this control is submitted + */ + virtual void GetFileName(nsAString& aFileName) = 0; + + /** + * Sets filename to be uploaded when this control is submitted + */ + virtual void SetFileName(const nsAString& aFileName) = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIFileControlElement, + NS_IFILECONTROLELEMENT_IID) + +#endif // nsIFileControlElement_h___ diff --git a/mozilla/content/html/content/src/nsHTMLInputElement.cpp b/mozilla/content/html/content/src/nsHTMLInputElement.cpp index 8a5b3aab2fb..0d3f1cb5825 100644 --- a/mozilla/content/html/content/src/nsHTMLInputElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLInputElement.cpp @@ -39,6 +39,7 @@ #include "nsIDOMHTMLInputElement.h" #include "nsIDOMNSHTMLInputElement.h" #include "nsITextControlElement.h" +#include "nsIFileControlElement.h" #include "nsIDOMNSEditableElement.h" #include "nsIRadioControlElement.h" #include "nsIRadioVisitor.h" @@ -149,7 +150,8 @@ class nsHTMLInputElement : public nsGenericHTMLFormElement, public nsITextControlElement, public nsIRadioControlElement, public nsIPhonetic, - public nsIDOMNSEditableElement + public nsIDOMNSEditableElement, + public nsIFileControlElement { public: nsHTMLInputElement(nsINodeInfo *aNodeInfo, PRBool aFromParser); @@ -217,6 +219,10 @@ public: // nsITextControlElement NS_IMETHOD TakeTextFrameValue(const nsAString& aValue); NS_IMETHOD SetValueChanged(PRBool aValueChanged); + + // nsIFileControlElement + virtual void GetFileName(nsAString& aFileName); + virtual void SetFileName(const nsAString& aFileName); // nsIRadioControlElement NS_IMETHOD RadioSetChecked(PRBool aNotify); @@ -231,7 +237,6 @@ public: */ virtual already_AddRefed GetRadioGroupContainer(); - protected: // Helper method nsresult SetValueInternal(const nsAString& aValue, @@ -323,6 +328,17 @@ protected: * The current value of the input if it has been changed from the deafault */ char* mValue; + /** + * The value of the input if it is a file input. This is the filename used + * when uploading a file. It is vital that this is kept separate from mValue + * so that it won't be possible to 'leak' the value from a text-input to a + * file-input. Additionally, the logic for this value is kept as simple as + * possible to avoid accidental errors where the wrong filename is used. + * Therefor the filename is always owned by this member, never by the frame. + * Whenever the frame wants to change the filename it has to call + * SetFileName to update this member. + */ + nsAutoPtr mFileName; }; // @@ -361,6 +377,7 @@ NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLInputElement, NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLInputElement) NS_INTERFACE_MAP_ENTRY(nsIDOMNSHTMLInputElement) NS_INTERFACE_MAP_ENTRY(nsITextControlElement) + NS_INTERFACE_MAP_ENTRY(nsIFileControlElement) NS_INTERFACE_MAP_ENTRY(nsIRadioControlElement) NS_INTERFACE_MAP_ENTRY(nsIPhonetic) NS_INTERFACE_MAP_ENTRY(imgIDecoderObserver) @@ -390,7 +407,6 @@ nsHTMLInputElement::Clone(nsINodeInfo *aNodeInfo, PRBool aDeep, switch (mType) { case NS_FORM_INPUT_TEXT: case NS_FORM_INPUT_PASSWORD: - case NS_FORM_INPUT_FILE: if (GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) { // We don't have our default value anymore. Set our value on // the clone. @@ -401,6 +417,11 @@ nsHTMLInputElement::Clone(nsINodeInfo *aNodeInfo, PRBool aDeep, it->SetValueInternal(value, nsnull); } break; + case NS_FORM_INPUT_FILE: + if (mFileName) { + it->mFileName = new nsString(*mFileName); + } + break; case NS_FORM_INPUT_RADIO: case NS_FORM_INPUT_CHECKBOX: if (GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)) { @@ -621,8 +642,7 @@ nsHTMLInputElement::SetSize(PRUint32 aValue) NS_IMETHODIMP nsHTMLInputElement::GetValue(nsAString& aValue) { - if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD || - mType == NS_FORM_INPUT_FILE) { + if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD) { // No need to flush here, if there's no frame created for this // input yet, there won't be a value in it (that we don't already // have) even if we force it to be created @@ -654,6 +674,17 @@ nsHTMLInputElement::GetValue(nsAString& aValue) return NS_OK; } + if (mType == NS_FORM_INPUT_FILE) { + if (mFileName) { + aValue = *mFileName; + } + else { + aValue.Truncate(); + } + + return NS_OK; + } + // Treat value == defaultValue for other input elements if (!GetAttr(kNameSpaceID_None, nsHTMLAtoms::value, aValue) && (mType == NS_FORM_INPUT_RADIO || mType == NS_FORM_INPUT_CHECKBOX)) { @@ -673,22 +704,28 @@ nsHTMLInputElement::SetValue(const nsAString& aValue) { // check security. Note that setting the value to the empty string is always // OK and gives pages a way to clear a file input if necessary. - if (mType == NS_FORM_INPUT_FILE && !aValue.IsEmpty()) { - nsIScriptSecurityManager *securityManager = - nsContentUtils::GetSecurityManager(); + if (mType == NS_FORM_INPUT_FILE) { + if (!aValue.IsEmpty()) { + nsIScriptSecurityManager *securityManager = + nsContentUtils::GetSecurityManager(); - PRBool enabled; - nsresult rv = - securityManager->IsCapabilityEnabled("UniversalFileRead", &enabled); - NS_ENSURE_SUCCESS(rv, rv); + PRBool enabled; + nsresult rv = + securityManager->IsCapabilityEnabled("UniversalFileRead", &enabled); + NS_ENSURE_SUCCESS(rv, rv); - if (!enabled) { - // setting the value of a "FILE" input widget requires the - // UniversalFileRead privilege - return NS_ERROR_DOM_SECURITY_ERR; + if (!enabled) { + // setting the value of a "FILE" input widget requires the + // UniversalFileRead privilege + return NS_ERROR_DOM_SECURITY_ERR; + } } + SetFileName(aValue); } - SetValueInternal(aValue, nsnull); + else { + SetValueInternal(aValue, nsnull); + } + return NS_OK; } @@ -703,12 +740,34 @@ nsHTMLInputElement::TakeTextFrameValue(const nsAString& aValue) return NS_OK; } +void +nsHTMLInputElement::GetFileName(nsAString& aValue) +{ + if (mFileName) { + aValue = *mFileName; + } + else { + aValue.Truncate(); + } +} + +void +nsHTMLInputElement::SetFileName(const nsAString& aValue) +{ + // No big deal if |new| fails, we simply won't submit the file + mFileName = aValue.IsEmpty() ? nsnull : new nsString(aValue); + + SetValueChanged(PR_TRUE); +} + nsresult nsHTMLInputElement::SetValueInternal(const nsAString& aValue, nsITextControlFrame* aFrame) { - if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD || - mType == NS_FORM_INPUT_FILE) { + NS_PRECONDITION(mType != NS_FORM_INPUT_FILE, + "Don't call SetValueInternal for file inputs"); + + if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD) { nsITextControlFrame* textControlFrame = aFrame; nsIFormControlFrame* formControlFrame = textControlFrame; @@ -726,9 +785,6 @@ nsHTMLInputElement::SetValueInternal(const nsAString& aValue, // File frames always own the value (if the frame is there). // Text frames have a bit that says whether they own the value. PRBool frameOwnsValue = PR_FALSE; - if (mType == NS_FORM_INPUT_FILE && formControlFrame) { - frameOwnsValue = PR_TRUE; - } if (textControlFrame) { textControlFrame->OwnsValue(&frameOwnsValue); } @@ -749,6 +805,10 @@ nsHTMLInputElement::SetValueInternal(const nsAString& aValue, return mValue ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } + if (mType == NS_FORM_INPUT_FILE) { + return NS_ERROR_UNEXPECTED; + } + // If the value of a hidden input was changed, we mark it changed so that we // will know we need to save / restore the value. Yes, we are overloading // the meaning of ValueChanged just a teensy bit to save a measly byte of @@ -1799,10 +1859,10 @@ nsHTMLInputElement::ParseAttribute(PRInt32 aNamespaceID, // process). PRInt8 newType = aResult.GetEnumValue(); if (newType == NS_FORM_INPUT_FILE) { - // If the type is being changed to file, set the element value - // to the empty string. This is for security. - // Call SetValueInternal so that this doesn't accidentally get caught - // in the security checks in SetValue. + // These calls aren't strictly needed any more since we'll never + // confuse values and filenames. However they're there for backwards + // compat. + SetFileName(EmptyString()); SetValueInternal(EmptyString(), nsnull); } @@ -2136,7 +2196,7 @@ nsHTMLInputElement::Reset() case NS_FORM_INPUT_FILE: { // Resetting it to blank should not perform security check - rv = SetValueInternal(EmptyString(), nsnull); + SetFileName(EmptyString()); break; } // Value is the same as defaultValue for hidden inputs @@ -2274,22 +2334,23 @@ nsHTMLInputElement::SubmitNamesValues(nsIFormSubmission* aFormSubmission, // nsCOMPtr file; - if (StringBeginsWith(value, NS_LITERAL_STRING("file:"), - nsCaseInsensitiveStringComparator())) { - // Converts the URL string into the corresponding nsIFile if possible. - // A local file will be created if the URL string begins with file://. - rv = NS_GetFileFromURLSpec(NS_ConvertUTF16toUTF8(value), - getter_AddRefs(file)); + if (mFileName) { + if (StringBeginsWith(*mFileName, NS_LITERAL_STRING("file:"), + nsCaseInsensitiveStringComparator())) { + // Converts the URL string into the corresponding nsIFile if possible. + // A local file will be created if the URL string begins with file://. + rv = NS_GetFileFromURLSpec(NS_ConvertUTF16toUTF8(*mFileName), + getter_AddRefs(file)); + } + if (!file) { + // this is no "file://", try as local file + nsCOMPtr localFile; + rv = NS_NewLocalFile(*mFileName, PR_FALSE, getter_AddRefs(localFile)); + file = localFile; + } } - if (!file) { - // this is no "file://", try as local file - nsCOMPtr localFile; - rv = NS_NewLocalFile(value, PR_FALSE, getter_AddRefs(localFile)); - file = localFile; - } - - if (NS_SUCCEEDED(rv)) { + if (file) { // // Get the leaf path name (to be submitted as the value) @@ -2359,7 +2420,7 @@ nsHTMLInputElement::SubmitNamesValues(nsIFormSubmission* aFormSubmission, // If we can't even make a truncated filename, submit empty string // rather than sending everything // - aFormSubmission->AddNameFilePair(this, name, value, + aFormSubmission->AddNameFilePair(this, name, EmptyString(), nsnull, NS_LITERAL_CSTRING("application/octet-stream"), PR_FALSE); return rv; @@ -2412,7 +2473,6 @@ nsHTMLInputElement::SaveState() case NS_FORM_INPUT_PASSWORD: break; case NS_FORM_INPUT_TEXT: - case NS_FORM_INPUT_FILE: case NS_FORM_INPUT_HIDDEN: { if (GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) { @@ -2431,6 +2491,17 @@ nsHTMLInputElement::SaveState() } break; } + case NS_FORM_INPUT_FILE: + { + if (mFileName) { + rv = GetPrimaryPresState(this, &state); + if (state) { + rv = state->SetStateProperty(NS_LITERAL_STRING("f"), *mFileName); + NS_ASSERTION(NS_SUCCEEDED(rv), "value save failed!"); + } + } + break; + } } if (GET_BOOLBIT(mBitField, BF_DISABLED_CHANGED)) { @@ -2514,7 +2585,6 @@ nsHTMLInputElement::RestoreState(nsPresState* aState) } case NS_FORM_INPUT_TEXT: - case NS_FORM_INPUT_FILE: case NS_FORM_INPUT_HIDDEN: { nsAutoString value; @@ -2525,6 +2595,16 @@ nsHTMLInputElement::RestoreState(nsPresState* aState) } break; } + case NS_FORM_INPUT_FILE: + { + nsAutoString value; + rv = aState->GetStateProperty(NS_LITERAL_STRING("f"), value); + NS_ASSERTION(NS_SUCCEEDED(rv), "value restore failed!"); + if (rv == NS_STATE_PROPERTY_EXISTS) { + SetFileName(value); + } + break; + } } nsAutoString disabled; diff --git a/mozilla/layout/forms/nsFileControlFrame.cpp b/mozilla/layout/forms/nsFileControlFrame.cpp index 4ecb73d3570..90916ba9aa9 100644 --- a/mozilla/layout/forms/nsFileControlFrame.cpp +++ b/mozilla/layout/forms/nsFileControlFrame.cpp @@ -66,7 +66,7 @@ #include "nsINodeInfo.h" #include "nsIDOMEventReceiver.h" #include "nsILocalFile.h" -#include "nsITextControlElement.h" +#include "nsIFileControlElement.h" #include "nsNodeInfoManager.h" #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" @@ -85,8 +85,7 @@ NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) nsFileControlFrame::nsFileControlFrame(nsStyleContext* aContext): nsAreaFrame(aContext), mTextFrame(nsnull), - mCachedState(nsnull), - mDidPreDestroy(PR_FALSE) + mCachedState(nsnull) { //Shrink the area around its contents SetFlags(NS_BLOCK_SHRINK_WRAP); @@ -110,54 +109,13 @@ nsFileControlFrame::~nsFileControlFrame() } } -void -nsFileControlFrame::PreDestroy() -{ - // Toss the value into the control from the anonymous content, which is about - // to get lost. Note that if the page is being torn down then the anonymous - // content may no longer have access to its frame. But _we_ can access that - // frame. So if it's there, get the value from the frame - if (mTextContent) { - nsAutoString value; - if (mTextFrame) { - // Second arg doesn't really matter here... - mTextFrame->GetValue(value, PR_TRUE); - } else { - // Get from the content - nsCOMPtr input = do_QueryInterface(mTextContent); - input->GetValue(value); - } - - // Have it take the value, just like when input type=text goes away - nsCOMPtr fileInput = do_QueryInterface(mContent); - fileInput->TakeTextFrameValue(value); - } - mDidPreDestroy = PR_TRUE; -} - void nsFileControlFrame::Destroy() { - if (!mDidPreDestroy) { - PreDestroy(); - } mTextFrame = nsnull; nsAreaFrame::Destroy(); } -void -nsFileControlFrame::RemovedAsPrimaryFrame() -{ - if (!mDidPreDestroy) { - PreDestroy(); - } -#ifdef DEBUG - else { - NS_ERROR("RemovedAsPrimaryFrame called after PreDestroy"); - } -#endif -} - NS_IMETHODIMP nsFileControlFrame::CreateAnonymousContent(nsPresContext* aPresContext, nsISupportsArray& aChildList) @@ -177,18 +135,17 @@ nsFileControlFrame::CreateAnonymousContent(nsPresContext* aPresContext, content.swap(mTextContent); - nsCOMPtr fileContent = do_QueryInterface(mContent); - if (mTextContent) { mTextContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::type, NS_LITERAL_STRING("text"), PR_FALSE); nsCOMPtr textControl = do_QueryInterface(mTextContent); if (textControl) { - if (fileContent) { + nsCOMPtr fileControl = do_QueryInterface(mContent); + if (fileControl) { // Initialize value when we create the content in case the value was set // before we got here nsAutoString value; - fileContent->GetValue(value); + fileControl->GetFileName(value); textControl->SetValue(value); } @@ -211,6 +168,7 @@ nsFileControlFrame::CreateAnonymousContent(nsPresContext* aPresContext, mBrowse = do_QueryInterface(content); if (mBrowse) { mBrowse->SetAttr(kNameSpaceID_None, nsHTMLAtoms::type, NS_LITERAL_STRING("button"), PR_FALSE); + nsCOMPtr fileContent = do_QueryInterface(mContent); nsCOMPtr browseControl = do_QueryInterface(mBrowse); if (fileContent && browseControl) { PRInt32 tabIndex; @@ -361,6 +319,11 @@ nsFileControlFrame::MouseClick(nsIDOMEvent* aMouseEvent) result = localFile->GetPath(unicodePath); if (!unicodePath.IsEmpty()) { mTextFrame->SetFormProperty(nsHTMLAtoms::value, unicodePath); + nsCOMPtr fileControl = do_QueryInterface(mContent); + if (fileControl) { + fileControl->SetFileName(unicodePath); + } + // May need to fire an onchange here mTextFrame->CheckFireOnChange(); return NS_OK; @@ -587,12 +550,10 @@ nsFileControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const if (mCachedState) { aValue.Assign(*mCachedState); } else if (mTextContent) { - nsCOMPtr textControl = - do_QueryInterface(mTextContent); - NS_ASSERTION(textControl, - " element not implementing nsIDOMHTMLInputElement?"); - - textControl->GetValue(aValue); + nsCOMPtr fileControl = do_QueryInterface(mTextContent); + if (fileControl) { + fileControl->GetFileName(aValue); + } } } return NS_OK; diff --git a/mozilla/layout/forms/nsFileControlFrame.h b/mozilla/layout/forms/nsFileControlFrame.h index 54fc7e615a3..d183b26d65d 100644 --- a/mozilla/layout/forms/nsFileControlFrame.h +++ b/mozilla/layout/forms/nsFileControlFrame.h @@ -75,8 +75,6 @@ public: const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); - virtual void RemovedAsPrimaryFrame(); - virtual void Destroy(); #ifdef NS_DEBUG @@ -193,16 +191,8 @@ private: void SyncAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRBool aWhichControls); - /** - * We call this when we are being destroyed or removed from the PFM. - * @param aPresContext the current pres context - */ - void PreDestroy(); - NS_IMETHOD_(nsrefcnt) AddRef() { return 1; } NS_IMETHOD_(nsrefcnt) Release() { return 1; } - - PRBool mDidPreDestroy; // has PreDestroy been called }; #endif