changed Release() to not check for external references to child form elements git-svn-id: svn://10.0.0.236/trunk@8036 18797224-902f-48f8-a5cc-f745e15eee43
1529 lines
42 KiB
C++
1529 lines
42 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#include "nsHTMLForms.h"
|
|
#include "nsIFormManager.h"
|
|
#include "nsIFormControl.h"
|
|
#include "nsIAtom.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsLeafFrame.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsDebug.h"
|
|
#include "nsIWidget.h"
|
|
#include "nsVoidArray.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsIHTMLAttributes.h"
|
|
#include "nsCRT.h"
|
|
#include "nsIURL.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsILinkHandler.h"
|
|
#include "nsInputRadio.h"
|
|
#include "nsIRadioButton.h"
|
|
#include "nsInputFile.h"
|
|
#include "nsDocument.h"
|
|
#include "nsHTMLContainer.h"
|
|
#include "nsIDOMHTMLFormElement.h"
|
|
#include "nsIDOMNSHTMLFormElement.h"
|
|
#include "nsIDOMHTMLCollection.h"
|
|
#include "nsIScriptObjectOwner.h"
|
|
|
|
#include "net.h"
|
|
#include "xp_file.h"
|
|
#include "prio.h"
|
|
#include "prmem.h"
|
|
#include "prenv.h"
|
|
|
|
// XXX Microsoft has a macro called GetClassName that conflicts
|
|
// with the DOM HTMLElement::GetClassName method
|
|
#ifdef GetClassName
|
|
#undef GetClassName
|
|
#endif
|
|
|
|
#define CRLF "\015\012"
|
|
|
|
// netlib has a general function (netlib\modules\liburl\src\escape.c)
|
|
// which does url encoding. Since netlib is not yet available for raptor,
|
|
// the following will suffice. Convert space to +, don't convert alphanumeric,
|
|
// conver each non alphanumeric char to %XY where XY is the hexadecimal
|
|
// equavalent of the binary representation of the character.
|
|
//
|
|
void URLEncode(char* aInString, char* aOutString)
|
|
{
|
|
if (nsnull == aInString) {
|
|
return;
|
|
}
|
|
static char *toHex = "0123456789ABCDEF";
|
|
char* outChar = aOutString;
|
|
for (char* inChar = aInString; *inChar; inChar++) {
|
|
if(' ' == *inChar) { // convert space to +
|
|
*outChar++ = '+';
|
|
} else if ( (((*inChar - '0') >= 0) && (('9' - *inChar) >= 0)) || // don't conver
|
|
(((*inChar - 'a') >= 0) && (('z' - *inChar) >= 0)) || // alphanumeric
|
|
(((*inChar - 'A') >= 0) && (('Z' - *inChar) >= 0)) ) {
|
|
*outChar++ = *inChar;
|
|
} else { // convert all else to hex
|
|
*outChar++ = '%';
|
|
*outChar++ = toHex[(*inChar >> 4) & 0x0F];
|
|
*outChar++ = toHex[*inChar & 0x0F];
|
|
}
|
|
}
|
|
*outChar = 0; // terminate the string
|
|
}
|
|
|
|
nsString* URLEncode(nsString& aString)
|
|
{
|
|
char* inBuf = aString.ToNewCString();
|
|
char* outBuf = new char[ (strlen(inBuf) * 3) + 1 ];
|
|
URLEncode(inBuf, outBuf);
|
|
nsString* result = new nsString(outBuf);
|
|
delete [] outBuf;
|
|
delete [] inBuf;
|
|
return result;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
static NS_DEFINE_IID(kIFormManagerIID, NS_IFORMMANAGER_IID);
|
|
static NS_DEFINE_IID(kIDOMNodeIID, NS_IDOMNODE_IID);
|
|
static NS_DEFINE_IID(kIDOMElementIID, NS_IDOMELEMENT_IID);
|
|
static NS_DEFINE_IID(kIDOMHTMLFormElementIID, NS_IDOMHTMLFORMELEMENT_IID);
|
|
static NS_DEFINE_IID(kIDOMNSHTMLFormElementIID, NS_IDOMNSHTMLFORMELEMENT_IID);
|
|
static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID);
|
|
|
|
class nsFormElementList;
|
|
class nsForm : public nsHTMLContainer, public nsIFormManager, public nsIDOMHTMLFormElement, public nsIDOMNSHTMLFormElement
|
|
{
|
|
public:
|
|
// Construct a new Form Element with no attributes. This needs to be
|
|
// made private and have a static COM create method.
|
|
nsForm(nsIAtom* aTag);
|
|
virtual ~nsForm();
|
|
|
|
void* operator new(size_t sz) {
|
|
void* rv = new char[sz];
|
|
nsCRT::zero(rv, sz);
|
|
return rv;
|
|
}
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
virtual void OnRadioChecked(nsIFormControl& aRadio);
|
|
|
|
// callback for reset button controls.
|
|
virtual void OnReset();
|
|
|
|
// callback for text and textarea controls. If there is a single
|
|
// text/textarea and a return is entered, then this is equavalent to
|
|
// a submit.
|
|
virtual void OnReturn();
|
|
|
|
// callback for submit button controls.
|
|
virtual void OnSubmit(nsIPresContext* aPresContext, nsIFrame* aFrame,
|
|
nsIFormControl* aSubmitter);
|
|
|
|
// callback for tabs on controls that can gain focus. This will
|
|
// eventually need to be handled at the document level to support
|
|
// the tabindex attribute.
|
|
virtual void OnTab();
|
|
|
|
virtual PRInt32 GetFormControlCount() const;
|
|
virtual nsIFormControl* GetFormControlAt(PRInt32 aIndex) const;
|
|
virtual PRBool AddFormControl(nsIFormControl* aFormControl);
|
|
virtual PRBool RemoveFormControl(nsIFormControl* aFormControl,
|
|
PRBool aChildIsRef = PR_TRUE);
|
|
|
|
virtual void SetAttribute(const nsString& aName, const nsString& aValue);
|
|
|
|
virtual nsContentAttr GetAttribute(const nsString& aName,
|
|
nsString& aResult) const;
|
|
|
|
virtual nsresult GetRefCount() const;
|
|
|
|
virtual void Init(PRBool aReinit);
|
|
|
|
virtual nsFormRenderingMode GetMode() const { return mRenderingMode; }
|
|
virtual void SetMode(nsFormRenderingMode aMode) { mRenderingMode = aMode; }
|
|
|
|
NS_FORWARD_IDOMNODE(nsHTMLContainer::)
|
|
NS_FORWARD_IDOMELEMENT(nsHTMLContainer::)
|
|
NS_FORWARD_IDOMHTMLELEMENT(nsHTMLContainer::)
|
|
|
|
NS_DECL_IDOMHTMLFORMELEMENT
|
|
NS_DECL_IDOMNSHTMLFORMELEMENT
|
|
|
|
NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
|
|
|
|
static nsString* gGET;
|
|
static nsString* gPOST;
|
|
static nsString* gMULTIPART;
|
|
|
|
|
|
protected:
|
|
void RemoveRadioGroups();
|
|
void ProcessAsURLEncoded(PRBool aIsPost, nsIFormControl* aSubmitter, nsString& aData);
|
|
void ProcessAsMultipart(nsIFormControl* aSubmitter, nsString& aData);
|
|
static const char* GetFileNameWithinPath(char* aPathName);
|
|
|
|
// the following are temporary until nspr and/or netlib provide them
|
|
static Temp_GetTempDir(char* aTempDirName);
|
|
static char* Temp_GenerateTempFileName(PRInt32 aMaxSize, char* aBuffer);
|
|
static void Temp_GetContentType(char* aPathName, char* aContentType);
|
|
|
|
nsIAtom* mTag;
|
|
nsIHTMLAttributes* mAttributes;
|
|
nsVoidArray mChildren;
|
|
nsVoidArray mRadioGroups;
|
|
nsString* mAction;
|
|
nsString* mEncoding;
|
|
nsString* mTarget;
|
|
PRInt32 mMethod;
|
|
PRBool mInited;
|
|
nsFormRenderingMode mRenderingMode;
|
|
nsFormElementList *mElements;
|
|
};
|
|
|
|
class nsFormElementList : public nsIDOMHTMLCollection, public nsIScriptObjectOwner {
|
|
public:
|
|
nsFormElementList(nsForm *aForm);
|
|
virtual ~nsFormElementList();
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
|
|
NS_IMETHOD ResetScriptObject();
|
|
|
|
// nsIDOMHTMLCollection interface
|
|
NS_DECL_IDOMHTMLCOLLECTION
|
|
|
|
// Called to tell us that the form is going away and that we
|
|
// should drop our (non ref-counted) reference to it
|
|
void ReleaseForm();
|
|
|
|
private:
|
|
nsForm *mForm;
|
|
void *mScriptObject;
|
|
};
|
|
|
|
|
|
#define METHOD_UNSET 0
|
|
#define METHOD_GET 1
|
|
#define METHOD_POST 2
|
|
|
|
// CLASS nsForm
|
|
|
|
nsString* nsForm::gGET = new nsString("get");
|
|
nsString* nsForm::gPOST = new nsString("post");
|
|
nsString* nsForm::gMULTIPART = new nsString("multipart/form-data");
|
|
|
|
// Note: operator new zeros our memory
|
|
nsForm::nsForm(nsIAtom* aTag) : nsHTMLContainer(aTag)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
mTag = aTag;
|
|
NS_IF_ADDREF(aTag);
|
|
mInited = PR_FALSE;
|
|
mElements = nsnull;
|
|
}
|
|
|
|
nsForm::~nsForm()
|
|
{
|
|
NS_IF_RELEASE(mTag);
|
|
int numChildren = GetFormControlCount();
|
|
for (int i = 0; i < numChildren; i++) {
|
|
nsIFormControl* child = GetFormControlAt(i);
|
|
if (child) {
|
|
child->SetFormManager(nsnull, PR_FALSE);
|
|
NS_RELEASE(child);
|
|
}
|
|
}
|
|
mChildren.Clear();
|
|
|
|
if (nsnull != mAction) delete mAction;
|
|
if (nsnull != mEncoding) delete mEncoding;
|
|
if (nsnull != mTarget) delete mTarget;
|
|
|
|
RemoveRadioGroups();
|
|
|
|
if (nsnull != mElements) {
|
|
mElements->ReleaseForm();
|
|
NS_RELEASE(mElements);
|
|
}
|
|
NS_IF_RELEASE(mAttributes);
|
|
}
|
|
|
|
nsresult nsForm::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
{
|
|
nsresult res = nsHTMLContainer::QueryInterface(aIID, aInstancePtr);
|
|
if (NS_NOINTERFACE == res) {
|
|
if (aIID.Equals(kIFormManagerIID)) {
|
|
*aInstancePtr = (void*)(nsIFormManager*)this;
|
|
AddRef();
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(kIDOMHTMLFormElementIID)) {
|
|
*aInstancePtr = (void*)(nsIDOMHTMLFormElement*)this;
|
|
AddRef();
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(kIDOMNSHTMLFormElementIID)) {
|
|
*aInstancePtr = (void*)(nsIDOMNSHTMLFormElement*)this;
|
|
AddRef();
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
nsrefcnt nsForm::AddRef(void)
|
|
{
|
|
PRInt32 refCnt = mRefCnt; // debugging
|
|
return ++mRefCnt;
|
|
}
|
|
|
|
nsrefcnt nsForm::GetRefCount() const
|
|
{
|
|
return mRefCnt;
|
|
}
|
|
|
|
nsrefcnt nsForm::Release()
|
|
{
|
|
--mRefCnt;
|
|
int numChildren = GetFormControlCount();
|
|
PRBool externalRefsToChildren = PR_FALSE; // are there refs to any children besides my ref
|
|
// XXX This check for external refs to child form elements can't work, because
|
|
// many other objects (e.g., frame, style context, parent content object) hold a
|
|
// reference to the child AND depending on order of destruction those references
|
|
// may or may not have been released yet...
|
|
#if 0
|
|
for (int i = 0; i < numChildren; i++) {
|
|
nsIFormControl* child = GetFormControlAt(i);
|
|
if (child && (child->GetRefCount() > 1)) {
|
|
externalRefsToChildren = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
if (!externalRefsToChildren && ((int)mRefCnt == numChildren)) {
|
|
mRefCnt = 0;
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return mRefCnt;
|
|
}
|
|
|
|
PRInt32
|
|
nsForm::GetFormControlCount() const
|
|
{
|
|
return mChildren.Count();
|
|
}
|
|
|
|
nsIFormControl*
|
|
nsForm::GetFormControlAt(PRInt32 aIndex) const
|
|
{
|
|
// do not addref ctl
|
|
nsIFormControl* ctl = (nsIFormControl*) mChildren.ElementAt(aIndex);
|
|
return ctl;
|
|
}
|
|
|
|
PRBool
|
|
nsForm::AddFormControl(nsIFormControl* aChild)
|
|
{
|
|
PRBool rv = mChildren.AppendElement(aChild);
|
|
if (rv) {
|
|
NS_ADDREF(aChild);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
PRBool
|
|
nsForm::RemoveFormControl(nsIFormControl* aChild, PRBool aChildIsRef)
|
|
{
|
|
PRBool rv = mChildren.RemoveElement(aChild);
|
|
if (rv && aChildIsRef) {
|
|
NS_RELEASE(aChild);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsForm::OnReset()
|
|
{
|
|
PRInt32 numChildren = mChildren.Count();
|
|
for (int childX = 0; childX < numChildren; childX++) {
|
|
nsIFormControl* child = (nsIFormControl*) mChildren.ElementAt(childX);
|
|
child->Reset();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsForm::OnReturn()
|
|
{
|
|
}
|
|
|
|
void DebugPrint(char* aLabel, nsString aString)
|
|
{
|
|
char* out = aString.ToNewCString();
|
|
printf("\n %s=%s\n", aLabel, out);
|
|
delete [] out;
|
|
}
|
|
|
|
void nsForm::OnSubmit(nsIPresContext* aPresContext, nsIFrame* aFrame,
|
|
nsIFormControl* aSubmitter)
|
|
{
|
|
nsString data; // this could be more efficient, by allocating a larger buffer
|
|
|
|
nsAutoString method, enctype;
|
|
GetAttribute("method", method);
|
|
GetAttribute("enctype", enctype);
|
|
|
|
PRBool isURLEncoded = (enctype.EqualsIgnoreCase(*gMULTIPART)) ? PR_FALSE : PR_TRUE;
|
|
|
|
// for enctype=multipart/form-data, force it to be post
|
|
// if method is "" (not specified) use "get" as default
|
|
PRBool isPost = (!method.EqualsIgnoreCase(*gPOST) && isURLEncoded) ? PR_FALSE : PR_TRUE;
|
|
|
|
if (isURLEncoded) {
|
|
ProcessAsURLEncoded(isPost, aSubmitter, data);
|
|
}
|
|
else {
|
|
ProcessAsMultipart(aSubmitter, data);
|
|
}
|
|
|
|
|
|
// make the url string
|
|
nsILinkHandler* handler;
|
|
if (NS_OK == aPresContext->GetLinkHandler(&handler)) {
|
|
// Resolve url to an absolute url
|
|
nsIURL* docURL = nsnull;
|
|
if (nsnull != mDocument) {
|
|
docURL = mDocument->GetDocumentURL();
|
|
}
|
|
|
|
nsAutoString target;
|
|
GetAttribute("target", target);
|
|
|
|
nsAutoString href;
|
|
GetAttribute("action", href);
|
|
if (!isPost) {
|
|
href.Append(data);
|
|
}
|
|
nsAutoString absURLSpec;
|
|
nsAutoString base;
|
|
nsresult rv = NS_MakeAbsoluteURL(docURL, base, href, absURLSpec);
|
|
NS_IF_RELEASE(docURL);
|
|
|
|
// Now pass on absolute url to the click handler
|
|
nsIPostData* postData = nsnull;
|
|
if (isPost) {
|
|
nsresult rv;
|
|
char* postBuffer = data.ToNewCString();
|
|
|
|
rv = NS_NewPostData(!isURLEncoded, postBuffer, &postData);
|
|
if (NS_OK != rv) {
|
|
delete [] postBuffer;
|
|
}
|
|
|
|
/* The postBuffer is now owned by the IPostData instance */
|
|
}
|
|
handler->OnLinkClick(aFrame, absURLSpec, target, postData);
|
|
NS_IF_RELEASE(postData);
|
|
NS_RELEASE(handler);
|
|
|
|
DebugPrint("url", absURLSpec);
|
|
DebugPrint("data", data);
|
|
}
|
|
}
|
|
|
|
void nsForm::ProcessAsURLEncoded(PRBool isPost, nsIFormControl* aSubmitter, nsString& aData)
|
|
{
|
|
nsString buf;
|
|
PRBool firstTime = PR_TRUE;
|
|
|
|
PRInt32 numChildren = mChildren.Count();
|
|
// collect and encode the data from the children controls
|
|
for (PRInt32 childX = 0; childX < numChildren; childX++) {
|
|
nsIFormControl* child = (nsIFormControl*) mChildren.ElementAt(childX);
|
|
if (child->IsSuccessful(aSubmitter)) {
|
|
PRInt32 numValues = 0;
|
|
PRInt32 maxNumValues = child->GetMaxNumValues();
|
|
if (maxNumValues <= 0) {
|
|
continue;
|
|
}
|
|
nsString* names = new nsString[maxNumValues];
|
|
nsString* values = new nsString[maxNumValues];
|
|
if (PR_TRUE == child->GetNamesValues(maxNumValues, numValues, values, names)) {
|
|
for (int valueX = 0; valueX < numValues; valueX++) {
|
|
if (PR_TRUE == firstTime) {
|
|
firstTime = PR_FALSE;
|
|
} else {
|
|
buf += "&";
|
|
}
|
|
nsString* convName = URLEncode(names[valueX]);
|
|
buf += *convName;
|
|
delete convName;
|
|
buf += "=";
|
|
nsString* convValue = URLEncode(values[valueX]);
|
|
buf += *convValue;
|
|
delete convValue;
|
|
}
|
|
}
|
|
delete [] names;
|
|
delete [] values;
|
|
}
|
|
}
|
|
|
|
aData.SetLength(0);
|
|
if (isPost) {
|
|
char size[16];
|
|
sprintf(size, "%d", buf.Length());
|
|
aData = "Content-type: application/x-www-form-urlencoded";
|
|
aData += CRLF;
|
|
aData += "Content-Length: ";
|
|
aData += size;
|
|
aData += CRLF;
|
|
aData += CRLF;
|
|
}
|
|
else {
|
|
aData += '?';
|
|
}
|
|
|
|
aData += buf;
|
|
}
|
|
|
|
// include the file name without the directory
|
|
const char* nsForm::GetFileNameWithinPath(char* aPathName)
|
|
{
|
|
char* fileNameStart = PL_strrchr(aPathName, '\\'); // windows
|
|
if (!fileNameStart) { // try unix
|
|
fileNameStart = PL_strrchr(aPathName, '\\');
|
|
}
|
|
if (fileNameStart) {
|
|
return fileNameStart+1;
|
|
}
|
|
else {
|
|
return aPathName;
|
|
}
|
|
}
|
|
|
|
// this needs to be provided by a higher level, since navigator might override
|
|
// the temp directory. XXX does not check that parm is big enough
|
|
nsForm::Temp_GetTempDir(char* aTempDirName)
|
|
{
|
|
aTempDirName[0] = 0;
|
|
const char* env;
|
|
|
|
if ((env = (const char *) getenv("TMP")) == nsnull) {
|
|
if ((env = (const char *) getenv("TEMP")) == nsnull) {
|
|
strcpy(aTempDirName, ".");
|
|
}
|
|
}
|
|
if (*env == '\0') { // null string means "."
|
|
strcpy(aTempDirName, ".");
|
|
}
|
|
if (0 == aTempDirName[0]) {
|
|
strcpy(aTempDirName, env);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// the following is a temporary measure until NET_cinfo_find_type or its
|
|
// replacement is available
|
|
void nsForm::Temp_GetContentType(char* aPathName, char* aContentType)
|
|
{
|
|
if (!aPathName) {
|
|
strcpy(aContentType, "unknown");
|
|
return;
|
|
}
|
|
|
|
int len = strlen(aPathName);
|
|
if (len <= 0) {
|
|
strcpy(aContentType, "unknown");
|
|
return;
|
|
}
|
|
|
|
char* fileExt = &aPathName[len-1];
|
|
for (int i = len-1; i >= 0; i--) {
|
|
if ('.' == aPathName[i]) {
|
|
break;
|
|
}
|
|
fileExt--;
|
|
}
|
|
if ((0 == nsCRT::strcasecmp(fileExt, ".html")) ||
|
|
(0 == nsCRT::strcasecmp(fileExt, ".htm"))) {
|
|
strcpy(aContentType, "text/html");
|
|
}
|
|
else if (0 == nsCRT::strcasecmp(fileExt, ".txt")) {
|
|
strcpy(aContentType, "text/plain");
|
|
}
|
|
else if (0 == nsCRT::strcasecmp(fileExt, ".gif")) {
|
|
strcpy(aContentType, "image/gif");
|
|
}
|
|
else if ((0 == nsCRT::strcasecmp(fileExt, ".jpeg")) ||
|
|
(0 == nsCRT::strcasecmp(fileExt, ".jpg"))) {
|
|
strcpy(aContentType, "image/jpeg");
|
|
}
|
|
else { // don't bother trying to do the others here
|
|
strcpy(aContentType, "unknown");
|
|
}
|
|
}
|
|
|
|
|
|
#if 0
|
|
char* tmpDir = aFileName; // copy name to fname */
|
|
// Keep generating file names till we find one that's not in use
|
|
while ((*env != '\0') && count++ {
|
|
*ptr++ = *env++;
|
|
}
|
|
if (ptr[-1] != '\\' && ptr[-1] != '/')
|
|
*ptr++ = '\\'; /* append backslash if not in env variable */
|
|
/* Append a suitable file name */
|
|
next_file_num++; /* advance counter */
|
|
sprintf(ptr, "JPG%03d.TMP", next_file_num);
|
|
/* Probe to see if file name is already in use */
|
|
if ((tfile = fopen(fname, READ_BINARY)) == NULL)
|
|
break;
|
|
fclose(tfile); /* oops, it's there; close tfile & try again */
|
|
}
|
|
|
|
char* tempDir = getenv("temp");
|
|
if (!tempDir) {
|
|
tempDir = getenv("tmp");
|
|
}
|
|
if (tempDir) {
|
|
return PR_TRUE;
|
|
}
|
|
else {
|
|
return PR_FALSE;
|
|
}
|
|
#endif
|
|
|
|
#define CONTENT_DISP "Content-Disposition: form-data; name=\""
|
|
#define FILENAME "\"; filename=\""
|
|
#define CONTENT_TYPE "Content-Type: "
|
|
#define CONTENT_ENCODING "Content-Encoding: "
|
|
#define BUFSIZE 1024
|
|
#define MULTIPART "multipart/form-data"
|
|
#define END "--"
|
|
|
|
void nsForm::ProcessAsMultipart(nsIFormControl* aSubmitter, nsString& aData)
|
|
{
|
|
aData.SetLength(0);
|
|
char buffer[BUFSIZE];
|
|
PRInt32 numChildren = mChildren.Count();
|
|
|
|
// construct a temporary file to put the data into
|
|
char tmpFileName[BUFSIZE];
|
|
char* result = Temp_GenerateTempFileName((PRInt32)BUFSIZE, tmpFileName);
|
|
if (!result) {
|
|
return;
|
|
}
|
|
|
|
PRFileDesc* tmpFile = PR_Open(tmpFileName, PR_CREATE_FILE | PR_WRONLY, 0644);
|
|
if (!tmpFile) {
|
|
return;
|
|
}
|
|
|
|
// write the content-type, boundary to the tmp file
|
|
char boundary[80];
|
|
sprintf(boundary, "-----------------------------%d%d%d",
|
|
boundary, rand(), rand(), rand());
|
|
sprintf(buffer, "Content-type: %s; boundary=%s" CRLF, MULTIPART, boundary);
|
|
PRInt32 len = PR_Write(tmpFile, buffer, PL_strlen(buffer));
|
|
if (len < 0) {
|
|
return;
|
|
}
|
|
|
|
PRInt32 boundaryLen = PL_strlen(boundary);
|
|
PRInt32 contDispLen = PL_strlen(CONTENT_DISP);
|
|
PRInt32 crlfLen = PL_strlen(CRLF);
|
|
|
|
// compute the content length, passing through all of the form controls
|
|
PRInt32 contentLen = crlfLen; // extra crlf after content-length header
|
|
|
|
PRInt32 childX; // stupid compiler
|
|
for (childX = 0; childX < numChildren; childX++) {
|
|
nsIFormControl* child = (nsIFormControl*) mChildren.ElementAt(childX);
|
|
nsAutoString type;
|
|
child->GetType(type);
|
|
if (child->IsSuccessful(aSubmitter)) {
|
|
PRInt32 numValues = 0;
|
|
PRInt32 maxNumValues = child->GetMaxNumValues();
|
|
if (maxNumValues <= 0) {
|
|
continue;
|
|
}
|
|
nsString* names = new nsString[maxNumValues];
|
|
nsString* values = new nsString[maxNumValues];
|
|
if (PR_FALSE == child->GetNamesValues(maxNumValues, numValues, values, names)) {
|
|
continue;
|
|
}
|
|
contentLen += boundaryLen + crlfLen;
|
|
contentLen += contDispLen;
|
|
for (int valueX = 0; valueX < numValues; valueX++) {
|
|
char* name = names[valueX].ToNewCString();
|
|
char* value = values[valueX].ToNewCString();
|
|
if ((0 == names[valueX].Length()) || (0 == values[valueX].Length())) {
|
|
continue;
|
|
}
|
|
contentLen += PL_strlen(name);
|
|
contentLen += 1 + crlfLen; // ending name quote plus CRLF
|
|
if (type.EqualsIgnoreCase(*nsInputFile::gFILE_TYPE)) { // <input type=file>
|
|
contentLen += PL_strlen(FILENAME);
|
|
|
|
// include the file name without the directory
|
|
char* fileNameStart = PL_strrchr(value, '/'); // unix
|
|
if (!fileNameStart) { // try windows
|
|
fileNameStart = PL_strrchr(value, '\\');
|
|
}
|
|
fileNameStart = (fileNameStart) ? fileNameStart+1 : value;
|
|
contentLen += PL_strlen(fileNameStart);
|
|
|
|
// determine the content-type of the file
|
|
|
|
char contentType[128];
|
|
Temp_GetContentType(value, &contentType[0]);
|
|
contentLen += PL_strlen(CONTENT_TYPE);
|
|
contentLen += PL_strlen(contentType) + crlfLen + crlfLen;
|
|
|
|
// get the size of the file
|
|
PRFileInfo fileInfo;
|
|
if (PR_SUCCESS == PR_GetFileInfo(value, &fileInfo)) {
|
|
contentLen += fileInfo.size;
|
|
}
|
|
}
|
|
else {
|
|
contentLen += PL_strlen(value) + crlfLen;
|
|
}
|
|
delete [] name;
|
|
delete [] value;
|
|
}
|
|
delete [] names;
|
|
delete [] values;
|
|
}
|
|
|
|
aData = tmpFileName;
|
|
}
|
|
|
|
contentLen += boundaryLen + PL_strlen(END) + crlfLen;
|
|
|
|
// write the content
|
|
sprintf(buffer, "Content-Length: %ld" CRLF CRLF, contentLen);
|
|
PR_Write(tmpFile, buffer, PL_strlen(buffer));
|
|
|
|
// write the content passing through all of the form controls a 2nd time
|
|
for (childX = 0; childX < numChildren; childX++) {
|
|
nsIFormControl* child = (nsIFormControl*) mChildren.ElementAt(childX);
|
|
nsAutoString type;
|
|
child->GetType(type);
|
|
if (child->IsSuccessful(aSubmitter)) {
|
|
PRInt32 numValues = 0;
|
|
PRInt32 maxNumValues = child->GetMaxNumValues();
|
|
if (maxNumValues <= 0) {
|
|
continue;
|
|
}
|
|
nsString* names = new nsString[maxNumValues];
|
|
nsString* values = new nsString[maxNumValues];
|
|
if (PR_FALSE == child->GetNamesValues(maxNumValues, numValues, values, names)) {
|
|
continue;
|
|
}
|
|
for (int valueX = 0; valueX < numValues; valueX++) {
|
|
char* name = names[valueX].ToNewCString();
|
|
char* value = values[valueX].ToNewCString();
|
|
if ((0 == names[valueX].Length()) || (0 == values[valueX].Length())) {
|
|
continue;
|
|
}
|
|
sprintf(buffer, "%s" CRLF, boundary);
|
|
PR_Write(tmpFile, buffer, PL_strlen(buffer));
|
|
PR_Write(tmpFile, CONTENT_DISP, contDispLen);
|
|
PR_Write(tmpFile, name, PL_strlen(name));
|
|
|
|
if (type.EqualsIgnoreCase(*nsInputFile::gFILE_TYPE)) { // <input type=file>
|
|
PR_Write(tmpFile, FILENAME, PL_strlen(FILENAME));
|
|
const char* fileNameStart = GetFileNameWithinPath(value);
|
|
PR_Write(tmpFile, fileNameStart, PL_strlen(fileNameStart));
|
|
}
|
|
PR_Write(tmpFile, "\"" CRLF, PL_strlen("\"" CRLF)); // end content disp
|
|
|
|
if (type.EqualsIgnoreCase(*nsInputFile::gFILE_TYPE)) {
|
|
// determine the content-type of the file
|
|
char contentType[128];
|
|
Temp_GetContentType(value, &contentType[0]);
|
|
PR_Write(tmpFile, CONTENT_TYPE, PL_strlen(CONTENT_TYPE));
|
|
PR_Write(tmpFile, contentType, PL_strlen(contentType));
|
|
PR_Write(tmpFile, CRLF, PL_strlen(CRLF));
|
|
PR_Write(tmpFile, CRLF, PL_strlen(CRLF)); // end content-type header
|
|
|
|
PRFileDesc* contentFile = PR_Open(value, PR_RDONLY, 0644);
|
|
if(contentFile) {
|
|
PRInt32 size;
|
|
while((size = PR_Read(contentFile, buffer, BUFSIZE)) > 0) {
|
|
PR_Write(tmpFile, buffer, size);
|
|
}
|
|
PR_Close(contentFile);
|
|
}
|
|
}
|
|
else {
|
|
PR_Write(tmpFile, value, PL_strlen(value));
|
|
PR_Write(tmpFile, CRLF, crlfLen);
|
|
}
|
|
delete [] name;
|
|
delete [] value;
|
|
}
|
|
delete [] names;
|
|
delete [] values;
|
|
}
|
|
}
|
|
|
|
sprintf(buffer, "%s--" CRLF, boundary);
|
|
PR_Write(tmpFile, buffer, PL_strlen(buffer));
|
|
|
|
PR_Close(tmpFile);
|
|
|
|
//StrAllocCopy(url_struct->post_data, tmpfilename);
|
|
//url_struct->post_data_is_file = TRUE;
|
|
}
|
|
|
|
void
|
|
nsForm::OnTab()
|
|
{
|
|
}
|
|
|
|
void nsForm::SetAttribute(const nsString& aName, const nsString& aValue)
|
|
{
|
|
nsAutoString tmp(aName);
|
|
tmp.ToUpperCase();
|
|
nsIAtom* atom = NS_NewAtom(tmp);
|
|
|
|
if (atom == nsHTMLAtoms::action) {
|
|
nsAutoString url(aValue);
|
|
url.StripWhitespace();
|
|
if (nsnull == mAction) {
|
|
mAction = new nsString(url);
|
|
}
|
|
else {
|
|
*mAction = url;
|
|
}
|
|
}
|
|
else if (atom == nsHTMLAtoms::encoding) {
|
|
if (nsnull == mEncoding) {
|
|
mEncoding = new nsString(aValue);
|
|
}
|
|
else {
|
|
*mEncoding = aValue;
|
|
}
|
|
}
|
|
else if (atom == nsHTMLAtoms::target) {
|
|
if (nsnull == mTarget) {
|
|
mTarget = new nsString(aValue);
|
|
}
|
|
else {
|
|
*mTarget = aValue;
|
|
}
|
|
}
|
|
else if (atom == nsHTMLAtoms::method) {
|
|
if (aValue.EqualsIgnoreCase("post")) {
|
|
mMethod = METHOD_POST;
|
|
}
|
|
else {
|
|
mMethod = METHOD_GET;
|
|
}
|
|
}
|
|
// temporarily, use type attribute to set the rendering mode
|
|
else if (atom == nsHTMLAtoms::type) {
|
|
mRenderingMode = (aValue.EqualsIgnoreCase("forward")) ? kForwardMode : kBackwardMode;
|
|
}
|
|
else {
|
|
// Use default storage for unknown attributes
|
|
if (nsnull == mAttributes) {
|
|
NS_NewHTMLAttributes(&mAttributes, nsnull);
|
|
}
|
|
if (nsnull != mAttributes) {
|
|
mAttributes->SetAttribute(atom, aValue);
|
|
}
|
|
}
|
|
NS_RELEASE(atom);
|
|
}
|
|
|
|
nsContentAttr nsForm::GetAttribute(const nsString& aName,
|
|
nsString& aResult) const
|
|
{
|
|
nsAutoString tmp(aName);
|
|
tmp.ToUpperCase();
|
|
nsIAtom* atom = NS_NewAtom(tmp);
|
|
nsContentAttr rv = eContentAttr_NoValue;
|
|
if (atom == nsHTMLAtoms::action) {
|
|
if (nsnull != mAction) {
|
|
aResult = *mAction;
|
|
rv = eContentAttr_HasValue;
|
|
}
|
|
}
|
|
else if (atom == nsHTMLAtoms::encoding) {
|
|
if (nsnull != mEncoding) {
|
|
aResult = *mEncoding;
|
|
rv = eContentAttr_HasValue;
|
|
}
|
|
}
|
|
else if (atom == nsHTMLAtoms::target) {
|
|
if (nsnull != mTarget) {
|
|
aResult = *mTarget;
|
|
rv = eContentAttr_HasValue;
|
|
}
|
|
}
|
|
else if (atom == nsHTMLAtoms::method) {
|
|
if (METHOD_UNSET != mMethod) {
|
|
if (METHOD_POST == mMethod) {
|
|
aResult = "post";
|
|
}
|
|
else {
|
|
aResult = "get";
|
|
}
|
|
rv = eContentAttr_HasValue;
|
|
}
|
|
}
|
|
else {
|
|
// Use default storage for unknown attributes
|
|
if (nsnull != mAttributes) {
|
|
nsHTMLValue value;
|
|
rv = mAttributes->GetAttribute(atom, value);
|
|
if (eContentAttr_HasValue == rv) {
|
|
if (value.GetUnit() == eHTMLUnit_String) {
|
|
value.GetStringValue(aResult);
|
|
}
|
|
else {
|
|
rv = eContentAttr_NoValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_RELEASE(atom);
|
|
return rv;
|
|
}
|
|
|
|
void nsForm::RemoveRadioGroups()
|
|
{
|
|
int numRadioGroups = mRadioGroups.Count();
|
|
for (int i = 0; i < numRadioGroups; i++) {
|
|
nsInputRadioGroup* radioGroup = (nsInputRadioGroup *) mRadioGroups.ElementAt(i);
|
|
delete radioGroup;
|
|
}
|
|
mRadioGroups.Clear();
|
|
}
|
|
|
|
void nsForm::Init(PRBool aReinit)
|
|
{
|
|
if (mInited && !aReinit) {
|
|
return;
|
|
}
|
|
mInited = PR_TRUE;
|
|
RemoveRadioGroups();
|
|
|
|
// determine which radio buttons belong to which radio groups, unnamed radio buttons
|
|
// don't go into any group since they can't be submitted. Determine which controls
|
|
// are capable of form submission.
|
|
PRInt32 textCount = 0;
|
|
nsIFormControl* textControl = nsnull;
|
|
int numControls = GetFormControlCount();
|
|
|
|
for (int i = 0; i < numControls; i++) {
|
|
nsIFormControl* control = (nsIFormControl *)GetFormControlAt(i);
|
|
nsString name;
|
|
control->GetName(name);
|
|
PRBool hasName = name.Length() > 0;
|
|
nsString type;
|
|
control->GetType(type);
|
|
|
|
// count text for determining "return" submission
|
|
if (type.EqualsIgnoreCase("text")) {
|
|
textCount++;
|
|
textControl = control;
|
|
}
|
|
|
|
// radio group processing
|
|
if (hasName && (type.Equals(*nsInputRadio::kTYPE))) { // XXX make constant consisten with above
|
|
int numGroups = mRadioGroups.Count();
|
|
PRBool added = PR_FALSE;
|
|
nsInputRadioGroup* group;
|
|
for (int j = 0; j < numGroups; j++) {
|
|
group = (nsInputRadioGroup *) mRadioGroups.ElementAt(j);
|
|
nsString groupName;
|
|
group->GetName(groupName);
|
|
if (groupName.Equals(name)) {
|
|
group->AddRadio(control);
|
|
added = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!added) {
|
|
group = new nsInputRadioGroup(name);
|
|
mRadioGroups.AppendElement(group);
|
|
group->AddRadio(control);
|
|
}
|
|
// allow only one checked radio button
|
|
if (control->GetChecked(PR_TRUE)) {
|
|
if (nsnull == group->GetCheckedRadio()) {
|
|
group->SetCheckedRadio(control);
|
|
}
|
|
else {
|
|
control->SetChecked(PR_FALSE, PR_TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there is only one text field, it can submit on "return"
|
|
if (1 == textCount) {
|
|
textControl->SetCanSubmit(PR_TRUE);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsForm::OnRadioChecked(nsIFormControl& aControl)
|
|
{
|
|
nsString radioName;
|
|
aControl.GetName(radioName);
|
|
if (0 == radioName.Length()) { // don't consider a radio without a name
|
|
return;
|
|
}
|
|
|
|
// locate the radio group with the name of aRadio
|
|
int numGroups = mRadioGroups.Count();
|
|
for (int j = 0; j < numGroups; j++) {
|
|
nsInputRadioGroup* group = (nsInputRadioGroup *) mRadioGroups.ElementAt(j);
|
|
nsString groupName;
|
|
group->GetName(groupName);
|
|
nsIFormControl* checkedRadio = group->GetCheckedRadio();
|
|
if (groupName.Equals(radioName) && (nsnull != checkedRadio) & (&aControl != checkedRadio)) {
|
|
checkedRadio->SetChecked(PR_FALSE, PR_FALSE);
|
|
group->SetCheckedRadio(&aControl);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetElements(nsIDOMHTMLCollection** aElements)
|
|
{
|
|
NS_PRECONDITION(nsnull != aElements, "null pointer");
|
|
if (nsnull == mElements) {
|
|
mElements = new nsFormElementList(this);
|
|
NS_ADDREF(mElements);
|
|
}
|
|
*aElements = mElements;
|
|
NS_ADDREF(mElements);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetName(nsString& aName)
|
|
{
|
|
// XXX The explicit class was required to get MSVC to be happy.
|
|
// It doesn't make sense.
|
|
((nsHTMLContainer *)this)->GetAttribute(nsHTMLAtoms::name, aName);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetAcceptCharset(nsString& aAcceptCharset)
|
|
{
|
|
((nsHTMLContainer *)this)->GetAttribute(nsHTMLAtoms::acceptcharset, aAcceptCharset);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::SetAcceptCharset(const nsString& aAcceptCharset)
|
|
{
|
|
((nsHTMLContainer *)this)->SetAttribute(nsHTMLAtoms::acceptcharset, aAcceptCharset);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetAction(nsString& aAction)
|
|
{
|
|
((nsHTMLContainer *)this)->GetAttribute(nsHTMLAtoms::action, aAction);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::SetAction(const nsString& aAction)
|
|
{
|
|
((nsHTMLContainer *)this)->SetAttribute(nsHTMLAtoms::action, aAction);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetEnctype(nsString& aEnctype)
|
|
{
|
|
((nsHTMLContainer *)this)->GetAttribute(nsHTMLAtoms::enctype, aEnctype);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::SetEnctype(const nsString& aEnctype)
|
|
{
|
|
((nsHTMLContainer *)this)->SetAttribute(nsHTMLAtoms::enctype, aEnctype);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetMethod(nsString& aMethod)
|
|
{
|
|
((nsHTMLContainer *)this)->GetAttribute(nsHTMLAtoms::method, aMethod);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::SetMethod(const nsString& aMethod)
|
|
{
|
|
((nsHTMLContainer *)this)->SetAttribute(nsHTMLAtoms::method, aMethod);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetTarget(nsString& aTarget)
|
|
{
|
|
((nsHTMLContainer *)this)->GetAttribute(nsHTMLAtoms::target, aTarget);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::SetTarget(const nsString& aTarget)
|
|
{
|
|
((nsHTMLContainer *)this)->SetAttribute(nsHTMLAtoms::target, aTarget);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::Reset()
|
|
{
|
|
OnReset();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::Submit()
|
|
{
|
|
// XXX Need to do something special with mailto: or news: URLs
|
|
if (nsnull != mDocument) {
|
|
nsIPresShell *shell = mDocument->GetShellAt(0);
|
|
if (nsnull != shell) {
|
|
nsIPresContext *context = shell->GetPresContext();
|
|
if (nsnull != context) {
|
|
// XXX We're currently passing in null for the frame and
|
|
// the submitter. It works for now, but might not always
|
|
// be correct. In the future, we might not need the
|
|
// frame to be passed to the link handler.
|
|
OnSubmit(context, nsnull, nsnull);
|
|
NS_RELEASE(context);
|
|
}
|
|
NS_RELEASE(shell);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetEncoding(nsString& aEncoding)
|
|
{
|
|
return GetEnctype(aEncoding);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::GetLength(PRUint32* aLength)
|
|
{
|
|
*aLength = mChildren.Count();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsForm::NamedItem(const nsString& aName, nsIDOMElement** aReturn)
|
|
{
|
|
// XXX For now we just search our element list. When forms become
|
|
// real content, we'll have to look at all our children, similar
|
|
// to the way HTMLDocuments do this.
|
|
PRInt32 i, count = GetFormControlCount();
|
|
nsresult result = NS_OK;
|
|
|
|
*aReturn = nsnull;
|
|
for (i = 0; i < count && *aReturn == nsnull; i++) {
|
|
nsIFormControl *control = (nsIFormControl *)GetFormControlAt(i);
|
|
if (nsnull != control) {
|
|
nsIContent *content;
|
|
|
|
result = control->QueryInterface(kIContentIID, (void **)&content);
|
|
if (NS_OK == result) {
|
|
nsAutoString name;
|
|
// XXX Should it be an EqualsIgnoreCase?
|
|
if (((content->GetAttribute("NAME", name) == eContentAttr_HasValue) &&
|
|
(aName.Equals(name))) ||
|
|
((content->GetAttribute("ID", name) == eContentAttr_HasValue) &&
|
|
(aName.Equals(name)))) {
|
|
result = control->QueryInterface(kIDOMElementIID, (void **)aReturn);
|
|
}
|
|
NS_RELEASE(content);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
nsresult
|
|
nsForm::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject)
|
|
{
|
|
nsresult res = NS_OK;
|
|
if (nsnull == mScriptObject) {
|
|
res = NS_NewScriptHTMLFormElement(aContext, this, nsnull, (void**)&mScriptObject);
|
|
}
|
|
*aScriptObject = mScriptObject;
|
|
return res;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewHTMLForm(nsIFormManager** aInstancePtrResult,
|
|
nsIAtom* aTag)
|
|
{
|
|
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
|
|
if (nsnull == aInstancePtrResult) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
nsForm* it = new nsForm(aTag);
|
|
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
nsresult result = it->QueryInterface(kIFormManagerIID, (void**) aInstancePtrResult);
|
|
return result;
|
|
}
|
|
|
|
// THE FOLLOWING WAS TAKEN FROM CMD/WINFE/FEGUI AND MODIFIED TO JUST
|
|
// GENERATE A TEMPFILE NAME.
|
|
|
|
|
|
// Windows _tempnam() lets the TMP environment variable override things sent in
|
|
// so it look like we're going to have to make a temp name by hand
|
|
//
|
|
// The user should *NOT* free the returned string. It is stored in static space
|
|
// and so is not valid across multiple calls to this function
|
|
//
|
|
// The names generated look like
|
|
// c:\netscape\cache\m0.moz
|
|
// c:\netscape\cache\m1.moz
|
|
// up to...
|
|
// c:\netscape\cache\m9999999.moz
|
|
// after that if fails
|
|
//
|
|
char* nsForm::Temp_GenerateTempFileName(PRInt32 aMaxSize, char* file_buf)
|
|
{
|
|
char directory[128];
|
|
Temp_GetTempDir(&directory[0]);
|
|
static char ext[] = ".TMP";
|
|
static char prefix[] = "nsform";
|
|
|
|
// We need to base our temporary file names on time, and not on sequential
|
|
// addition because of the cache not being updated when the user
|
|
// crashes and files that have been deleted are over written with
|
|
// other files; bad data.
|
|
// The 52 valid DOS file name characters are
|
|
// 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_^$~!#%&-{}@`'()
|
|
// We will only be using the first 32 of the choices.
|
|
//
|
|
// Time name format will be M+++++++.MOZ
|
|
// Where M is the single letter prefix (can be longer....)
|
|
// Where +++++++ is the 7 character time representation (a full 8.3
|
|
// file name will be made).
|
|
// Where .MOZ is the file extension to be used.
|
|
//
|
|
// In the event that the time requested is the same time as the last call
|
|
// to this function, then the current time is incremented by one,
|
|
// as is the last time called to facilitate unique file names.
|
|
// In the event that the invented file name already exists (can't
|
|
// really happen statistically unless the clock is messed up or we
|
|
// manually incremented the time), then the times are incremented
|
|
// until an open name can be found.
|
|
//
|
|
// time_t (the time) has 32 bits, or 4,294,967,296 combinations.
|
|
// We will map the 32 bits into the 7 possible characters as follows:
|
|
// Starting with the lsb, sets of 5 bits (32 values) will be mapped
|
|
// over to the appropriate file name character, and then
|
|
// incremented to an approprate file name character.
|
|
// The left over 2 bits will be mapped into the seventh file name
|
|
// character.
|
|
//
|
|
|
|
int i_letter, i_timechars, i_numtries = 0;
|
|
char ca_time[8];
|
|
time_t this_call = (time_t)0;
|
|
|
|
// We have to base the length of our time string on the length
|
|
// of the incoming prefix....
|
|
//
|
|
i_timechars = 8 - strlen(prefix);
|
|
|
|
// Infinite loop until the conditions are satisfied.
|
|
// There is no danger, unless every possible file name is used.
|
|
//
|
|
while(1) {
|
|
// We used to use the time to generate this.
|
|
// Now, we use some crypto to avoid bug #47027
|
|
//RNG_GenerateGlobalRandomBytes((void *)&this_call, sizeof(this_call));
|
|
char* output=(char *)&this_call;
|
|
size_t len = sizeof(this_call);
|
|
size_t i;
|
|
srand((unsigned int) PR_IntervalToMilliseconds(PR_IntervalNow()));
|
|
for (i=0;i<len; i++) {
|
|
int t = rand();
|
|
*output = (char) (t % 256);
|
|
output++;
|
|
}
|
|
|
|
// Convert the time into a 7 character string.
|
|
// Strip only the signifigant 5 bits.
|
|
// We'll want to reverse the string to make it look coherent
|
|
// in a directory of file names.
|
|
//
|
|
for(i_letter = 0; i_letter < i_timechars; i_letter++) {
|
|
ca_time[i_letter] = (char)((this_call >> (i_letter * 5)) & 0x1F);
|
|
|
|
// Convert any numbers to their equivalent ascii code
|
|
//
|
|
if(ca_time[i_letter] <= 9) {
|
|
ca_time[i_letter] += '0';
|
|
}
|
|
// Convert the character to it's equivalent ascii code
|
|
//
|
|
else {
|
|
ca_time[i_letter] += 'A' - 10;
|
|
}
|
|
}
|
|
|
|
// End the created time string.
|
|
//
|
|
ca_time[i_letter] = '\0';
|
|
|
|
// Reverse the time string.
|
|
//
|
|
// XXX fix this
|
|
#ifdef XP_PC
|
|
_strrev(ca_time);
|
|
#endif
|
|
// Create the fully qualified path and file name.
|
|
//
|
|
sprintf(file_buf, "%s\\%s%s%s", directory, prefix, ca_time, ext);
|
|
|
|
// Determine if the file exists, and mark that we've tried yet
|
|
// another file name (mark to be used later).
|
|
//
|
|
// Use the system call instead of XP_Stat since we already
|
|
// know the name and we don't want recursion
|
|
//
|
|
// XXX fix this
|
|
#ifdef XP_PC
|
|
struct _stat statinfo;
|
|
int status = _stat(file_buf, &statinfo);
|
|
#else
|
|
int status = 1;
|
|
#endif
|
|
i_numtries++;
|
|
|
|
// If it does not exists, we are successful, return the name.
|
|
//
|
|
if(status == -1) {
|
|
// TRACE("Temp file name is %s\n", file_buf);
|
|
return(file_buf);
|
|
}
|
|
|
|
// If there is no room for additional characters in the time,
|
|
// we'll have to return NULL here, or we go infinite.
|
|
// This is a one case scenario where the requested prefix is
|
|
// actually 8 letters long.
|
|
// Infinite loops could occur with a 7, 6, 5, etc character prefixes
|
|
// if available files are all eaten up (rare to impossible), in
|
|
// which case, we should check at some arbitrary frequency of
|
|
// tries before we give up instead of attempting to Vulcanize
|
|
// this code. Live long and prosper.
|
|
//
|
|
if(i_timechars == 0) {
|
|
break;
|
|
}
|
|
else if(i_numtries == 0x00FF) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Requested name is thought to be impossible to generate.
|
|
//
|
|
//TRACE("No more temp file names....\n");
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
//
|
|
// Implementation of nsFormElementList
|
|
//
|
|
|
|
nsFormElementList::nsFormElementList(nsForm *aForm) : mForm(aForm)
|
|
{
|
|
// Note that we don't add a reference to the form (to avoid
|
|
// circular references). The form will tell us if it's going
|
|
// away.
|
|
mRefCnt = 0;
|
|
mScriptObject = nsnull;
|
|
}
|
|
|
|
nsFormElementList::~nsFormElementList()
|
|
{
|
|
}
|
|
|
|
nsresult nsFormElementList::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|
{
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
|
|
static NS_DEFINE_IID(kIDOMHTMLCollectionIID, NS_IDOMHTMLCOLLECTION_IID);
|
|
static NS_DEFINE_IID(kIScriptObjectOwnerIID, NS_ISCRIPTOBJECTOWNER_IID);
|
|
if (aIID.Equals(kIDOMHTMLCollectionIID)) {
|
|
*aInstancePtr = (void*)(nsIDOMHTMLCollection*)this;
|
|
AddRef();
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(kIScriptObjectOwnerIID)) {
|
|
*aInstancePtr = (void*)(nsIScriptObjectOwner*)this;
|
|
AddRef();
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(kISupportsIID)) {
|
|
*aInstancePtr = (void*)(nsISupports*)(nsIDOMHTMLCollection*)this;
|
|
AddRef();
|
|
return NS_OK;
|
|
}
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
NS_IMPL_ADDREF(nsFormElementList)
|
|
NS_IMPL_RELEASE(nsFormElementList)
|
|
|
|
|
|
nsresult nsFormElementList::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject)
|
|
{
|
|
nsresult res = NS_OK;
|
|
if (nsnull == mScriptObject) {
|
|
res = NS_NewScriptHTMLCollection(aContext, this, nsnull, (void**)&mScriptObject);
|
|
}
|
|
*aScriptObject = mScriptObject;
|
|
return res;
|
|
}
|
|
|
|
nsresult nsFormElementList::ResetScriptObject()
|
|
{
|
|
mScriptObject = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIDOMHTMLCollection interface
|
|
NS_IMETHODIMP
|
|
nsFormElementList::GetLength(PRUint32* aLength)
|
|
{
|
|
if (nsnull != mForm) {
|
|
*aLength = mForm->GetFormControlCount();
|
|
}
|
|
else {
|
|
*aLength = 0;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFormElementList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
|
|
{
|
|
nsIFormControl *control = nsnull;
|
|
nsresult res = NS_OK;
|
|
if (nsnull != mForm) {
|
|
control = mForm->GetFormControlAt(aIndex);
|
|
if (nsnull != control) {
|
|
res = control->QueryInterface(kIDOMNodeIID, (void**)aReturn);
|
|
}
|
|
else {
|
|
*aReturn = nsnull;
|
|
}
|
|
}
|
|
else {
|
|
*aReturn = nsnull;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFormElementList::NamedItem(const nsString& aName, nsIDOMNode** aReturn)
|
|
{
|
|
PRInt32 i, count = mForm->GetFormControlCount();
|
|
nsresult result = NS_OK;
|
|
|
|
*aReturn = nsnull;
|
|
for (i = 0; i < count && *aReturn == nsnull; i++) {
|
|
nsIFormControl *control = (nsIFormControl *)mForm->GetFormControlAt(i);
|
|
if (nsnull != control) {
|
|
nsIContent *content;
|
|
|
|
result = control->QueryInterface(kIContentIID, (void **)&content);
|
|
if (NS_OK == result) {
|
|
nsAutoString name;
|
|
// XXX Should it be an EqualsIgnoreCase?
|
|
if (((content->GetAttribute("NAME", name) == eContentAttr_HasValue) &&
|
|
(aName.Equals(name))) ||
|
|
((content->GetAttribute("ID", name) == eContentAttr_HasValue) &&
|
|
(aName.Equals(name)))) {
|
|
result = control->QueryInterface(kIDOMNodeIID, (void **)aReturn);
|
|
}
|
|
NS_RELEASE(content);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
nsFormElementList::ReleaseForm()
|
|
{
|
|
if (nsnull != mForm) {
|
|
mForm = nsnull;
|
|
}
|
|
}
|