/* -*- 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.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/NPL/
 *
 * 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 Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 *   Pierre Phaneuf <pp@ludusdesign.com>
 */



#define NS_IMPL_IDS
#include "nsICharsetConverterManager.h"
#include "nsICharsetAlias.h"
#include "nsIPlatformCharset.h"
#undef NS_IMPL_IDS 

#include "nsCOMPtr.h"
#include "nsXPIDLString.h"

#include "nsID.h"


#include "nsFormFrame.h"
#include "nsIFormControlFrame.h"
#include "nsFormControlFrame.h"
#include "nsFileControlFrame.h"
#include "nsRadioControlGroup.h"

#include "nsIForm.h"
#include "nsIFormControl.h"
#include "nsIAtom.h"
#include "nsHTMLIIDs.h"
#include "nsIRenderingContext.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIStyleContext.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 "nsIHTMLDocument.h"

#include "nsIFormProcessor.h"

#include "nsIIOService.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
#include "nsIDocument.h"
#include "nsILinkHandler.h"
#include "nsGfxRadioControlFrame.h"
#include "nsDocument.h"
#include "nsIDOMHTMLFormElement.h"
#include "nsIDOMNSHTMLFormElement.h"
#include "nsDOMError.h"
#include "nsHTMLParts.h"
#include "nsIReflowCommand.h"

#include "nsIUnicodeEncoder.h"

// FormSubmit observer notification
#include "nsIFormSubmitObserver.h"
#include "nsIObserverService.h"
#include "nsIServiceManager.h"

// Get base target for submission
#include "nsIHTMLContent.h"
#include "nsIDOMHTMLInputElement.h"

#include "net.h"
#include "xp_file.h"
#include "prio.h"
#include "prmem.h"
#include "prenv.h"
#include "prlong.h"

// Rewrite of Multipart form posting
#include "nsSpecialSystemDirectory.h"
#include "nsIFileSpec.h"
#include "nsFileSpec.h"
#include "nsIProtocolHandler.h"
#include "nsIHTTPProtocolHandler.h"
#include "nsIServiceManager.h"
#include "nsEscape.h"
#include "nsLinebreakConverter.h"
#include "nsCExternalHandlerService.h"
#include "nsIMIMEService.h"

#include "nsILocalFile.h"		// Using nsILocalFile to get file size

// Security
#include "nsIScriptSecurityManager.h"

#include "nsIDOMWindowInternal.h"

static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
static NS_DEFINE_CID(kPlatformCharsetCID, NS_PLATFORMCHARSET_CID);

//----------------------------------------------------------------------

static NS_DEFINE_IID(kIFrameIID, NS_IFRAME_IID);
static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);

NS_IMETHODIMP
nsFormFrame::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
  if (aIID.Equals(NS_GET_IID(nsIFormManager))) {
    *aInstancePtr = (void*)(nsIFormManager*)this;
    return NS_OK;
  }
  return nsBlockFrame::QueryInterface(aIID, aInstancePtr);
}

nsrefcnt nsFormFrame::AddRef(void)
{
  NS_ERROR("not supported");
  return 0;
}

nsrefcnt nsFormFrame::Release(void)
{
  NS_ERROR("not supported");
  return 0;
}

nsFormFrame::nsFormFrame()
  : nsBlockFrame()
{
  mTextSubmitter = nsnull;
  mTextSubmitterSet = PR_FALSE;
}

nsFormFrame::~nsFormFrame()
{
  RemoveRadioGroups();
  PRInt32 numControls = mFormControls.Count();
  PRInt32 i;
  // Traverse list from end to 0 -> void array remove method does less work
  for (i = (numControls-1); i>=0; i--) {
    nsIFormControlFrame* fcFrame = (nsIFormControlFrame*) mFormControls.ElementAt(i);
    fcFrame->SetFormFrame(nsnull);
    mFormControls.RemoveElement(fcFrame);
  }
}

PRBool 
nsFormFrame::CanSubmit(nsIFormControlFrame *aFrame)
{ 
  if (!aFrame)
    return PR_FALSE;
  if (mTextSubmitter == aFrame) {
    return PR_TRUE;
  }
  PRInt32 type;
  aFrame->GetType(&type);
  if ((NS_FORM_INPUT_SUBMIT == type) || (NS_FORM_INPUT_IMAGE == type)) {
    return PR_TRUE;
  }
  return PR_FALSE;
}

NS_IMETHODIMP
nsFormFrame::GetAction(nsString* aAction)
{
  nsresult result = NS_OK;
  if (mContent) {
    nsIDOMHTMLFormElement* form = nsnull;
    result = mContent->QueryInterface(NS_GET_IID(nsIDOMHTMLFormElement), (void**)&form);
    if ((NS_OK == result) && form) {
      form->GetAction(*aAction);
      NS_RELEASE(form);
    }
  }
  return result;
}

NS_IMETHODIMP
nsFormFrame::GetTarget(nsString* aTarget)
{
  nsresult result = NS_OK;
  if (mContent) {
    nsIDOMHTMLFormElement* form = nsnull;
    result = mContent->QueryInterface(NS_GET_IID(nsIDOMHTMLFormElement), (void**)&form);
    if ((NS_OK == result) && form) {
      form->GetTarget(*aTarget);
      if ((*aTarget).Length() == 0) {
        nsIHTMLContent* content = nsnull;
	result = form->QueryInterface(kIHTMLContentIID, (void**)&content);
	if ((NS_OK == result) && content) {
	  content->GetBaseTarget(*aTarget);
	  NS_RELEASE(content);
	}
      }
      NS_RELEASE(form);
    }
  }
  return result;
}

NS_IMETHODIMP
nsFormFrame::GetMethod(PRInt32* aMethod)
{
  nsresult result = NS_OK;
  if (mContent) {
    nsIHTMLContent* form = nsnull;
    result = mContent->QueryInterface(kIHTMLContentIID, (void**)&form);
    if ((NS_OK == result) && form) {
      nsHTMLValue value;
      if (NS_CONTENT_ATTR_HAS_VALUE == (form->GetHTMLAttribute(nsHTMLAtoms::method, value))) {
        if (eHTMLUnit_Enumerated == value.GetUnit()) {
          *aMethod = value.GetIntValue();
          NS_RELEASE(form);
          return NS_OK;
        }
      }
      NS_RELEASE(form);
    }
  }
  *aMethod = NS_FORM_METHOD_GET;
  return result;
}

NS_IMETHODIMP
nsFormFrame::GetEnctype(PRInt32* aEnctype)
{
  nsresult result = NS_OK;
  if (mContent) {
    nsIHTMLContent* form = nsnull;
    result = mContent->QueryInterface(kIHTMLContentIID, (void**)&form);
    if ((NS_OK == result) && form) {
      nsHTMLValue value;
      if (NS_CONTENT_ATTR_HAS_VALUE == (form->GetHTMLAttribute(nsHTMLAtoms::enctype, value))) {
        if (eHTMLUnit_Enumerated == value.GetUnit()) {
          *aEnctype = value.GetIntValue();
          NS_RELEASE(form);
          return NS_OK;
        }
      }
      NS_RELEASE(form);
    }
  }
  *aEnctype = NS_FORM_ENCTYPE_URLENCODED;
  return result;
}

NS_IMETHODIMP 
nsFormFrame::OnReset(nsIPresContext* aPresContext)
{
  PRInt32 numControls = mFormControls.Count();
  for (int i = 0; i < numControls; i++) {
    nsIFormControlFrame* fcFrame = (nsIFormControlFrame*) mFormControls.ElementAt(i);
    fcFrame->Reset(aPresContext);
  }
  return NS_OK;
}

void nsFormFrame::RemoveRadioGroups()
{
  int numRadioGroups = mRadioGroups.Count();
  for (int i = 0; i < numRadioGroups; i++) {
    nsRadioControlGroup* radioGroup = (nsRadioControlGroup *) mRadioGroups.ElementAt(i);
    delete radioGroup;
  }
  mRadioGroups.Clear();
}

void nsFormFrame::AddFormControlFrame(nsIPresContext* aPresContext, nsIFrame& aFrame)
{
  // Make sure we have a form control
  nsIFormControlFrame* fcFrame = nsnull;
  nsresult result = aFrame.QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
  if ((NS_OK != result) || (nsnull == fcFrame)) {
    return;
  }

  // Get this control's form frame and add this control to it
  nsCOMPtr<nsIContent> iContent;
  result = aFrame.GetContent(getter_AddRefs(iContent));
  if (NS_SUCCEEDED(result) && iContent) {
    nsCOMPtr<nsIFormControl> formControl;
    result = iContent->QueryInterface(NS_GET_IID(nsIFormControl), getter_AddRefs(formControl));
    if (NS_SUCCEEDED(result) && formControl) {
      nsCOMPtr<nsIDOMHTMLFormElement> formElem;
      result = formControl->GetForm(getter_AddRefs(formElem));
      if (NS_SUCCEEDED(result) && formElem) {
        nsCOMPtr<nsIPresShell> presShell;
        result = aPresContext->GetShell(getter_AddRefs(presShell));
        if (NS_SUCCEEDED(result) && presShell) {
          nsIContent* formContent;
          result = formElem->QueryInterface(NS_GET_IID(nsIContent), (void**)&formContent);
          if (NS_SUCCEEDED(result) && formContent) {
            nsFormFrame* formFrame = nsnull;
            result = presShell->GetPrimaryFrameFor(formContent, (nsIFrame**)&formFrame);
            if (NS_SUCCEEDED(result) && formFrame) {
              fcFrame->SetFormFrame(formFrame);
              formFrame->AddFormControlFrame(aPresContext, *fcFrame);
            }
            NS_RELEASE(formContent);
          }
        }
      }
    }
  }
}

// a solo text control can be a submitter (if return is hit)
void nsFormFrame::UpdateSubmitter(nsIFormControlFrame * aFrame) {
  PRInt32 type;
  aFrame->GetType(&type);
  if ((NS_FORM_INPUT_TEXT == type) || (NS_FORM_INPUT_PASSWORD == type)) {
    // Only set if this is the first text input found.  Otherwise, set to null.
    if (!mTextSubmitterSet) {
      mTextSubmitter = aFrame;
      mTextSubmitterSet = PR_TRUE;
    } else mTextSubmitter = nsnull;
  }
}

void nsFormFrame::RemoveFormControlFrame(nsIFormControlFrame& aFrame)
{
  // Remove form control from array
  mFormControls.RemoveElement(&aFrame);

  // Bug 45540: If this is mTextSubmitter, reset the value by walking mFormControls.
  if (mTextSubmitter == &aFrame) {
    mTextSubmitterSet = PR_FALSE;
    for (PRInt32 i=0; i<mFormControls.Count(); i++) {
      UpdateSubmitter((nsIFormControlFrame *)mFormControls.ElementAt(i));
      if (mTextSubmitterSet)
        break;
    }
  }
}

NS_IMETHODIMP
nsFormFrame::RemoveFrame(nsIPresContext* aPresContext,
                         nsIPresShell&   aPresShell,
                         nsIAtom*        aListName,
                         nsIFrame*       aOldFrame)
{
  nsIFormControlFrame* fcFrame = nsnull;
  nsresult result = aOldFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
  if ((NS_OK == result) || (nsnull != fcFrame)) {
    PRInt32 type;
    fcFrame->GetType(&type);
    if (NS_FORM_INPUT_RADIO == type) {
      nsRadioControlGroup * group;
      nsAutoString name;
      nsresult rv = GetRadioInfo(fcFrame, name, group);
      if (NS_SUCCEEDED(rv) && nsnull != group) {
        DoDefaultSelection(aPresContext, group, (nsGfxRadioControlFrame*)fcFrame);
      }
    }
  }

  return nsBlockFrame::RemoveFrame(aPresContext, aPresShell, aListName, aOldFrame);
}

nsresult nsFormFrame::GetRadioInfo(nsIFormControlFrame* aFrame,
                                   nsString& aName,
                                   nsRadioControlGroup *& aGroup)
{
  aGroup = nsnull;
  aName.SetLength(0);
  aFrame->GetName(&aName);
  PRBool hasName = aName.Length() > 0;

  // radio group processing
  if (hasName) { 
    int numGroups = mRadioGroups.Count();
    nsRadioControlGroup* group;
    for (int j = 0; j < numGroups; j++) {
      group = (nsRadioControlGroup *) mRadioGroups.ElementAt(j);
      nsString groupName;
      group->GetName(groupName);
      if (groupName.Equals(aName)) {
        aGroup = group;
        return NS_OK;
      }
    }
  }
  return NS_ERROR_FAILURE;
}

void nsFormFrame::DoDefaultSelection(nsIPresContext*          aPresContext, 
                                     nsRadioControlGroup *    aGroup,
                                     nsGfxRadioControlFrame * aRadioToIgnore)
{
#if 1
  // If in standard mode, then a radio group MUST default 
  // to the first item in the group (it must be selected)
  nsCompatibility mode;
  aPresContext->GetCompatibilityMode(&mode);
  if (eCompatibility_Standard == mode) {
    // first find out if any have a default selection
    PRInt32 i;
    PRInt32 numItems = aGroup->GetNumRadios();
    PRBool oneIsDefSelected = PR_FALSE;
    for (i=0;i<numItems;i++) {
      nsGfxRadioControlFrame * radioBtn = (nsGfxRadioControlFrame*) aGroup->GetRadioAt(i);
      nsCOMPtr<nsIContent> content;
      radioBtn->GetContent(getter_AddRefs(content));
      if (content) {
        nsCOMPtr<nsIDOMHTMLInputElement> input(do_QueryInterface(content));
        if (input) {
          PRBool initiallyChecked = PR_FALSE;
          if (radioBtn->IsRestored()) {
            initiallyChecked = radioBtn->GetRestoredChecked();
          } else {
            initiallyChecked = radioBtn->GetDefaultChecked();
          }

          PRBool currentValue = radioBtn->GetChecked();
          if (currentValue != initiallyChecked ) {
            input->SetChecked(initiallyChecked);
            //radioBtn->SetChecked(aPresContext, initSelected, PR_FALSE);
          }
          if (initiallyChecked) {
            oneIsDefSelected = PR_TRUE;
          }
        }
      }
    }

    // if there isn't a default selcted radio then 
    // select the firdst one in the group.
    // if aRadioToIgnore is not null then it is being deleted
    // so don't select that item, select the next one if there is one.
    if (!oneIsDefSelected && numItems > 0) {
      nsGfxRadioControlFrame * radioBtn = (nsGfxRadioControlFrame*) aGroup->GetRadioAt(0);
      if (aRadioToIgnore != nsnull && aRadioToIgnore == radioBtn) {
        if (numItems == 1) {
          return;
        }
        radioBtn = (nsGfxRadioControlFrame*) aGroup->GetRadioAt(1);
      }
      nsCOMPtr<nsIContent> content;
      radioBtn->GetContent(getter_AddRefs(content));
      if (content) {
        nsCOMPtr<nsIDOMHTMLInputElement> input(do_QueryInterface(content));
        if (input) {
          input->SetChecked(PR_TRUE);
        }
      }
    }
  }
#endif
}


void nsFormFrame::AddFormControlFrame(nsIPresContext* aPresContext, nsIFormControlFrame& aFrame)
{
  // Add this control to the list
  // Sort by content ID - this assures we submit in document order (bug 18728)
  PRInt32 i = mFormControls.Count();

  nsCOMPtr<nsIContent> newContent;
  nsIFrame* newFrame = nsnull;
  nsresult rv = aFrame.QueryInterface(kIFrameIID, (void **)&newFrame);
  if (NS_SUCCEEDED(rv) && newFrame) {
    rv = newFrame->GetContent(getter_AddRefs(newContent));
    if (NS_SUCCEEDED(rv) && newContent) {
      PRUint32 newID;
      newContent->GetContentID(&newID);
      for (; i>0; i--) {
        nsIFormControlFrame* thisControl = (nsIFormControlFrame*) mFormControls.ElementAt(i-1);
        if (thisControl) {
          nsCOMPtr<nsIContent> thisContent;
          nsIFrame* thisFrame = nsnull;
          rv = thisControl->QueryInterface(kIFrameIID, (void **)&thisFrame);
          if (NS_SUCCEEDED(rv) && thisFrame) {
            rv = thisFrame->GetContent(getter_AddRefs(thisContent));
            if (NS_SUCCEEDED(rv) && thisContent) {
              PRUint32 thisID;
              thisContent->GetContentID(&thisID);
              if (newID > thisID)
                break;
            }
          }
        }
      }
    }
  }
  mFormControls.InsertElementAt(&aFrame, i);

  // 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.

  UpdateSubmitter(&aFrame);

  // radio group processing
  PRInt32 type;
  aFrame.GetType(&type);
  if (NS_FORM_INPUT_RADIO == type) { 
    nsGfxRadioControlFrame* radioFrame = (nsGfxRadioControlFrame*)&aFrame;
    // gets the name of the radio group and the group
    nsRadioControlGroup * group;
    nsAutoString name;
    rv = GetRadioInfo(&aFrame, name, group);
    if (NS_SUCCEEDED(rv) && nsnull != group) {
      group->AddRadio(radioFrame);
    } else {
      group = new nsRadioControlGroup(name);
      mRadioGroups.AppendElement(group);
      group->AddRadio(radioFrame);
    }
    // allow only one checked radio button
    PRBool initiallyChecked = PR_FALSE;
    if (radioFrame->IsRestored()) {
      initiallyChecked = radioFrame->GetRestoredChecked();
    } else {
      initiallyChecked = radioFrame->GetDefaultChecked();
    }
    if (initiallyChecked) {
	    if (nsnull == group->GetCheckedRadio()) {
	      group->SetCheckedRadio(radioFrame);
	    } else {
	      radioFrame->SetChecked(aPresContext, PR_FALSE, PR_FALSE);
	    }
    }
    DoDefaultSelection(aPresContext, group);
  }
}

void nsFormFrame::RemoveRadioControlFrame(nsIFormControlFrame * aFrame)
{
  PRInt32 type;
  aFrame->GetType(&type);

  // radio group processing
  if (NS_FORM_INPUT_RADIO == type) { 
    nsGfxRadioControlFrame* radioFrame = (nsGfxRadioControlFrame*)aFrame;
    // gets the name of the radio group and the group
    nsRadioControlGroup * group;
    nsAutoString name;
    nsresult rv = GetRadioInfo(aFrame, name, group);
    if (NS_SUCCEEDED(rv) && nsnull != group) {
      group->RemoveRadio(radioFrame);
    }
  }
}

  
//--------------------------------------------------------
// Return the content of the currently selected item in
// the radio group of the incoming radiobutton.
//--------------------------------------------------------
nsresult
nsFormFrame::GetRadioGroupSelectedContent(nsGfxRadioControlFrame* aControl,
                                          nsIContent **           aRadiobtn)
{
  NS_ASSERTION(aControl, "nsGfxRadioControlFrame can't be null");

  // first get correct interface
  nsIFormControlFrame* fcFrame = nsnull;
  nsresult result = aControl->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
  if (NS_SUCCEEDED(result)) {
    // get the form frame for the radio btn
    nsFormFrame * formFrame = ((nsFormControlFrame *)aControl)->GetFormFrame();
    if (formFrame != nsnull) {
      // now get the radio group by name
      nsAutoString groupName;
      nsRadioControlGroup * group = nsnull;
      result = formFrame->GetRadioInfo(fcFrame, groupName, group);
      if (NS_SUCCEEDED(result) && nsnull != group) {
        // get the currently checked radio button
        nsGfxRadioControlFrame* currentCheckBtn = group->GetCheckedRadio();
        if (currentCheckBtn != nsnull) {
          currentCheckBtn->GetContent(aRadiobtn);
        }
      }
    }
  }
  return NS_OK;
}

//--------------------------------------------------------
// returns NS_ERROR_FAILURE if the radiobtn doesn't have a group
// returns NS_OK is it did have a radio group 
//--------------------------------------------------------
nsresult
nsFormFrame::OnRadioChecked(nsIPresContext*         aPresContext, 
                            nsGfxRadioControlFrame& aControl, 
                            PRBool                  aNewCheckedVal)
{
  // first get correct interface
  nsIFormControlFrame* fcFrame = nsnull;
  nsresult result = aControl.QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
  if (NS_SUCCEEDED(result)) {
    // now get the radio group by name
    nsAutoString groupName;
    nsRadioControlGroup * group = nsnull;
    result = GetRadioInfo(fcFrame, groupName, group);
    if (NS_SUCCEEDED(result) && nsnull != group) {
      // get the currently checked radio button
      nsGfxRadioControlFrame* currentCheckBtn = group->GetCheckedRadio();
      // is the new checked btn different than the current button?
      if (&aControl != currentCheckBtn) {
        // if the new button is being set to false
        // then don't do anything
        if (aNewCheckedVal) {
          // the current btn could be null
          if (currentCheckBtn != nsnull) {
            currentCheckBtn->SetChecked(aPresContext, !aNewCheckedVal, PR_FALSE);
          }
          aControl.SetChecked(aPresContext, aNewCheckedVal, PR_FALSE);
          group->SetCheckedRadio(&aControl);
        }
      } else {
        // here we are setting the same radio button 
        // as the one that is currently checked 
        //
        if (currentCheckBtn != nsnull) {
          currentCheckBtn->SetChecked(aPresContext, aNewCheckedVal, PR_FALSE);
          // So if we are setting the current btn to be 0 or off 
          // then we must set a default selction
          if (!aNewCheckedVal) {
            DoDefaultSelection(aPresContext, group, currentCheckBtn);
          }
        }
      }
    }
  }
  return NS_OK;
}


nsresult
NS_NewFormFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aFlags)
{
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
  if (nsnull == aNewFrame) {
    return NS_ERROR_NULL_POINTER;
  }
  nsFormFrame* it = new (aPresShell) nsFormFrame;
  if (!it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  it->SetFlags(aFlags);
  *aNewFrame = it;
  return NS_OK;
}

#ifdef NS_DEBUG

PRIVATE
void DebugPrint(char* aLabel, nsString aString)
{
  char* out = aString.ToNewCString();
  printf("\n %s=%s\n", aLabel, out);
  delete [] out;
}

#endif

// Give the form process observer a chance to modify the value passed for form submission
nsresult
nsFormFrame::ProcessValue(nsIFormProcessor& aFormProcessor, nsIFormControlFrame* aFrameControl, const nsString& aName, nsString& aValue)
{
  nsresult res = NS_OK;

  nsIFrame *frame = nsnull;
  res = aFrameControl->QueryInterface(kIFrameIID, (void **)&frame);
  if (NS_SUCCEEDED(res) && (frame)) {
    nsCOMPtr<nsIContent> content;
    nsresult rv = frame->GetContent(getter_AddRefs(content));
    if (NS_SUCCEEDED(rv) && content) {
      nsCOMPtr<nsIDOMHTMLElement> formElement;
      res = content->QueryInterface(NS_GET_IID(nsIDOMHTMLElement), getter_AddRefs(formElement));
      if (NS_SUCCEEDED(res) && formElement) {
	 	    res = aFormProcessor.ProcessValue(formElement, aName, aValue);
		    NS_ASSERTION(NS_SUCCEEDED(res), "unable Notify form process observer"); 
      }
    }
  }

  return res;
}


// submission

NS_IMETHODIMP
nsFormFrame::OnSubmit(nsIPresContext* aPresContext, nsIFrame* aFrame)
{
  if (!mContent) {
    return NS_FORM_NOTOK;
  }

   // Get a service to process the value part of the form data
   // If one doesn't exist, that fine. It's not required.
  nsresult result = NS_OK;
  NS_WITH_SERVICE(nsIFormProcessor, formProcessor, kFormProcessorCID, &result)

  nsString data; // this could be more efficient, by allocating a larger buffer

  PRInt32 method, enctype;
  GetMethod(&method);
  GetEnctype(&enctype);

  PRBool isURLEncoded = (NS_FORM_ENCTYPE_MULTIPART != enctype);

  // for enctype=multipart/form-data, force it to be post
  // if method is "" (not specified) use "get" as default
  PRBool isPost = (NS_FORM_METHOD_POST == method) || !isURLEncoded; 

  nsIFormControlFrame* fcFrame = nsnull;

  // Since JS Submit() calls are not linked to an element, aFrame is null.
  // fcframe will remain null, but IsSuccess will return succes in this case.
  if (aFrame != nsnull) {
    aFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
  }

  nsIFileSpec* multipartDataFile = nsnull;
  if (isURLEncoded) {
    result = ProcessAsURLEncoded(formProcessor, isPost, data, fcFrame);
  }
  else {
    result = ProcessAsMultipart(formProcessor, multipartDataFile, fcFrame);
  }

  // Don't bother submitting form if we failed to generate a valid submission
  if (NS_FAILED(result)) {
    NS_IF_RELEASE(multipartDataFile);
    return result;
  }

  // make the url string
  nsCOMPtr<nsILinkHandler> handler;
  if (NS_OK == aPresContext->GetLinkHandler(getter_AddRefs(handler))) {
    nsAutoString href;
    GetAction(&href);

    // Get the document.
    // We'll need it now to form the URL we're submitting to.
    // We'll also need it later to get the DOM window when notifying form submit observers (bug 33203)
    nsCOMPtr<nsIDocument> document;
    mContent->GetDocument(*getter_AddRefs(document));
    if (!document) return NS_OK; // No doc means don't submit, see Bug 28988

    // Resolve url to an absolute url
    nsCOMPtr<nsIURI> docURL;
    document->GetBaseURL(*getter_AddRefs(docURL));
    NS_ASSERTION(docURL, "No Base URL found in Form Submit!\n");
    if (!docURL) return NS_OK; // No base URL -> exit early, see Bug 30721

      // If an action is not specified and we are inside 
      // a HTML document then reload the URL. This makes us
      // compatible with 4.x browsers.
      // If we are in some other type of document such as XML or
      // XUL, do nothing. This prevents undesirable reloading of
      // a document inside XUL.

    if (href.IsEmpty()) {
      nsCOMPtr<nsIHTMLDocument> htmlDoc;
      if (PR_FALSE == NS_SUCCEEDED(document->QueryInterface(NS_GET_IID(nsIHTMLDocument),
                                             getter_AddRefs(htmlDoc)))) {   
        // Must be a XML, XUL or other non-HTML document type
        // so do nothing.
        return NS_OK;
      } 

      // Necko's MakeAbsoluteURI doesn't reuse the baseURL's rel path if it is
      // passed a zero length rel path.
      nsXPIDLCString relPath;
      docURL->GetSpec(getter_Copies(relPath));
      NS_ASSERTION(relPath, "Rel path couldn't be formed in form submit!\n");
      if (relPath) {
        href.AppendWithConversion(relPath);

      } else {
        result = NS_ERROR_OUT_OF_MEMORY;
      }
    } else {
      // Get security manager, check to see if access to action URI is allowed.
      NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager,
                      NS_SCRIPTSECURITYMANAGER_CONTRACTID, &result);
      nsCOMPtr<nsIURI> actionURL;
      if (NS_FAILED(result)) return result;

      result = NS_NewURI(getter_AddRefs(actionURL), href, docURL);
      if (NS_SUCCEEDED(result)) {
        result = securityManager->CheckLoadURI(docURL, actionURL, nsIScriptSecurityManager::STANDARD);
        if (NS_FAILED(result)) return result;
      }

      nsXPIDLCString scheme;
      if (actionURL && NS_FAILED(result = actionURL->GetScheme(getter_Copies(scheme))))
        return result;
      if (nsCRT::strcmp(scheme, "mailto") == 0) {
        PRBool enabled;
        if (NS_FAILED(result = securityManager->IsCapabilityEnabled("UniversalSendMail", 
                                                                    &enabled)))
        {
          return result;
        }
        if (!enabled) {
          // Form submit to a mailto: URI requires the UniversalSendMail privilege
          return NS_ERROR_DOM_SECURITY_ERR;
        }
      }
    }

    nsAutoString target;
    GetTarget(&target);

    // Add the URI encoded form values to the URI
    // Get the scheme of the URI.
    nsCOMPtr<nsIURI> actionURL;
    nsXPIDLCString scheme;
    if (NS_SUCCEEDED(result = NS_NewURI(getter_AddRefs(actionURL), href, docURL))) {
      result = actionURL->GetScheme(getter_Copies(scheme));
    }
    nsAutoString theScheme; theScheme.AssignWithConversion( NS_STATIC_CAST(const char*, scheme) );
    // Append the URI encoded variable/value pairs for GET's
    if (!isPost) {
      if (!theScheme.EqualsIgnoreCase("javascript")) { // Not for JS URIs, see bug 26917

        // Bug 42616: Trim off named anchor and save it to add later
        PRInt32 namedAnchorPos = href.FindChar('#', PR_FALSE, 0);
        nsAutoString namedAnchor;
        if (kNotFound != namedAnchorPos) {
          href.Right(namedAnchor, (href.Length() - namedAnchorPos));
          href.Truncate(namedAnchorPos);
        }

        // Chop off old query string (bug 25330, 57333)
        // Only do this for GET not POST (bug 41585)
        PRInt32 queryStart = href.FindChar('?');
        if (kNotFound != queryStart) {
          href.Truncate(queryStart);
        }

        href.Append(PRUnichar('?'));        
        href.Append(data);

        // Bug 42616: Add named anchor to end after query string
        if (namedAnchor.Length()) {
          href.Append(namedAnchor);
        }
      }
    }

    nsAutoString absURLSpec;
    result = NS_MakeAbsoluteURI(absURLSpec, href, docURL);
    if (NS_FAILED(result)) return result;

    // Notify observers that the form is being submitted.
    result = NS_OK;
    NS_WITH_SERVICE(nsIObserverService, service, NS_OBSERVERSERVICE_CONTRACTID, &result);
    if (NS_FAILED(result)) return result;

    nsString  theTopic; theTopic.AssignWithConversion(NS_FORMSUBMIT_SUBJECT);
    nsCOMPtr<nsIEnumerator> theEnum;
    result = service->EnumerateObserverList(theTopic.GetUnicode(), getter_AddRefs(theEnum));
    if (NS_SUCCEEDED(result) && theEnum){
      nsCOMPtr<nsISupports> inst;
      PRBool cancelSubmit = PR_FALSE;

      nsCOMPtr<nsIScriptGlobalObject> globalObject;
      document->GetScriptGlobalObject(getter_AddRefs(globalObject));  
      nsCOMPtr<nsIDOMWindowInternal> window = do_QueryInterface(globalObject);

      for (theEnum->First(); theEnum->IsDone() != NS_OK; theEnum->Next()) {
        result = theEnum->CurrentItem(getter_AddRefs(inst));
        if (NS_SUCCEEDED(result) && inst) {
          nsCOMPtr<nsIFormSubmitObserver> formSubmitObserver = do_QueryInterface(inst, &result);
          if (NS_SUCCEEDED(result) && formSubmitObserver) {
            nsresult notifyStatus = formSubmitObserver->Notify(mContent, window, actionURL, &cancelSubmit);
            if (NS_FAILED(notifyStatus)) { // assert/warn if we get here?
              return notifyStatus;
            }
          }
        }
        if (cancelSubmit) {
          return NS_OK;
        }
      }
    }

    // Now pass on absolute url to the click handler
    nsIInputStream* postDataStream = nsnull;
    if (isPost) {
      nsresult rv;
      nsCAutoString postBuffer;
      postBuffer.AssignWithConversion(data);

      if (isURLEncoded) {
        rv = NS_NewPostDataStream(&postDataStream, !isURLEncoded, postBuffer.GetBuffer(), 0);
      } else {
// Cut-and-paste of NS_NewPostDataStream
        NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
        if (NS_FAILED(rv)) return rv;
	nsCOMPtr<nsIProtocolHandler> pHandler;
	rv = serv->GetProtocolHandler("http", getter_AddRefs(pHandler));
	if (NS_FAILED(rv)) return rv;
	
	nsCOMPtr<nsIHTTPProtocolHandler> http = do_QueryInterface(pHandler, &rv);
	if (NS_FAILED(rv)) return rv;
	
// Cut-and-paste of nsHTTPHandler::NewPostDataStream inside if(isFile)
        nsIInputStream* rawStream = nsnull; // Strong
        rv = multipartDataFile->GetInputStream(&rawStream); // AddRef
	if (NS_FAILED(rv)) return rv;
	
	rv = http->NewEncodeStream(rawStream, nsIHTTPProtocolHandler::ENCODE_NORMAL, &postDataStream);
        NS_RELEASE(rawStream);
// End Cut-and-pastisms
      }

      /* The postBuffer is now owned by the IPostData instance */
    }    
    if (handler) {
#if defined(DEBUG_rods) || defined(DEBUG_pollmann)
      {
        printf("******\n");
        char * str = data.ToNewCString();
        printf("postBuffer[%s]\n", str);
        Recycle(str);

        str = absURLSpec.ToNewCString();
        printf("absURLSpec[%s]\n", str);
        Recycle(str);

        str = target.ToNewCString();
        printf("target    [%s]\n", str);
        Recycle(str);
        printf("******\n");
      }
#endif
      handler->OnLinkClick(mContent, eLinkVerb_Replace,
                           absURLSpec.GetUnicode(),
                           target.GetUnicode(), postDataStream);
    }
// We need to delete the data file somewhere...
//    if (!isURLEncoded) {
//      nsFileSpec mdf = nsnull;
//      result = multipartDataFile->GetFileSpec(&mdf);
//      if (NS_SUCCEEDED(result) && mdf) {
//        mdf.Delete(PR_FALSE);
//      }
//    }
// XXX DON'T NS_IF_RELEASE(postDataStream), this happens in Necko!

// If you need these for debugging...
// wrap them in DEBUG_<username>
// Printing the data and url prints the contents of passwords
//DebugPrint("url", absURLSpec);
//DebugPrint("data", data);
  }
  return result;
}

// XXX i18n helper routines
char*
nsFormFrame::UnicodeToNewBytes(const PRUnichar* aSrc, PRUint32 aLen, nsIUnicodeEncoder* encoder)
{
   char* res = nsnull;
   if(NS_SUCCEEDED(encoder->Reset()))
   {
      PRInt32 maxByteLen = 0;
      if(NS_SUCCEEDED(encoder->GetMaxLength(aSrc, (PRInt32) aLen, &maxByteLen))) 
      {
          res = new char[maxByteLen+1];
          if(nsnull != res) 
          {
             PRInt32 reslen = maxByteLen;
             PRInt32 reslen2 ;
             PRInt32 srclen = aLen;
             encoder->Convert(aSrc, &srclen, res, &reslen);
             reslen2 = maxByteLen-reslen;
             encoder->Finish(res+reslen, &reslen2);
             res[reslen+reslen2] = '\0';
          }
      }

   }
   return res;
}

// XXX i18n helper routines
nsString*
nsFormFrame::URLEncode(const nsString& aString, nsIUnicodeEncoder* encoder) 
{
  char* inBuf = nsnull;
  if(encoder)
    inBuf  = UnicodeToNewBytes(aString.GetUnicode(), aString.Length(), encoder);

  if(nsnull == inBuf)
    inBuf  = aString.ToNewCString();

  // convert to CRLF breaks
  char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks(inBuf,
                           nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakNet);
  delete [] inBuf;
  
  char* outBuf = nsEscape(convertedBuf, url_XPAlphas);
  nsString* result = new nsString;
  result->AssignWithConversion(outBuf);
  nsCRT::free(outBuf);
  nsMemory::Free(convertedBuf);
  return result;
}

void nsFormFrame::GetSubmitCharset(nsString& oCharset)
{
  oCharset.AssignWithConversion("UTF-8"); // default to utf-8
  nsresult rv;
  // XXX
  // We may want to get it from the HTML 4 Accept-Charset attribute first
  // see 17.3 The FORM element in HTML 4 for details
  nsresult result = NS_OK;
  nsAutoString acceptCharsetValue;
  if (mContent) {
    nsIHTMLContent* form = nsnull;
    result = mContent->QueryInterface(kIHTMLContentIID, (void**)&form);
    if (NS_SUCCEEDED(result) && (nsnull != form)) {
      nsHTMLValue value;
      result = form->GetHTMLAttribute(nsHTMLAtoms::acceptcharset, value);
      if (NS_CONTENT_ATTR_HAS_VALUE == result) {
        if (eHTMLUnit_String == value.GetUnit()) {
          value.GetStringValue(acceptCharsetValue);
        }
      }
      NS_RELEASE(form);
    }
  }
#ifdef DEBUG_ftang
  printf("accept-charset = %s\n", acceptCharsetValue.ToNewUTF8String());
#endif
  PRInt32 l = acceptCharsetValue.Length();
  if(l > 0 ) {
    PRInt32 offset=0;
    PRInt32 spPos=0;
    // get charset from charsets one by one
    NS_WITH_SERVICE(nsICharsetAlias, calias, kCharsetAliasCID, &rv);
    if(NS_SUCCEEDED(rv) && (nsnull != calias)) {
      do {
        spPos = acceptCharsetValue.FindChar(PRUnichar(' '),PR_TRUE, offset);
        PRInt32 cnt = ((-1==spPos)?(l-offset):(spPos-offset));
        if(cnt > 0) {
          nsAutoString charset;
          acceptCharsetValue.Mid(charset, offset, cnt);
#ifdef DEBUG_ftang
          printf("charset[i] = %s\n",charset.ToNewUTF8String());
#endif
          if(NS_SUCCEEDED(calias->GetPreferred(charset,oCharset)))
            return;
        }
        offset = spPos + 1;
      } while(spPos != -1);
    }
  }
  // if there are no accept-charset or all the charset are not supported
  // Get the charset from document
  nsIDocument* doc = nsnull;
  mContent->GetDocument(doc);
  if( nsnull != doc ) {
    rv = doc->GetDocumentCharacterSet(oCharset);
    NS_RELEASE(doc);
  }

}

NS_IMETHODIMP nsFormFrame::GetEncoder(nsIUnicodeEncoder** encoder)
{
  *encoder = nsnull;
  nsAutoString charset;
  nsresult rv = NS_OK;
  GetSubmitCharset(charset);
#ifdef DEBUG_ftang
  printf("charset=%s\n", charset.ToNewCString());
#endif
  
  // Get Charset, get the encoder.
  nsICharsetConverterManager * ccm = nsnull;
  rv = nsServiceManager::GetService(kCharsetConverterManagerCID ,
                                    NS_GET_IID(nsICharsetConverterManager),
                                    (nsISupports**)&ccm);
  if(NS_SUCCEEDED(rv) && (nsnull != ccm)) {
     rv = ccm->GetUnicodeEncoder(&charset, encoder);
     nsServiceManager::ReleaseService( kCharsetConverterManagerCID, ccm);
     if (nsnull == encoder) {
       rv = NS_ERROR_FAILURE;
     }
     if (NS_SUCCEEDED(rv)) {
       rv = (*encoder)->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nsnull, (PRUnichar)'?');
     }
  }
  return NS_OK;
}

NS_IMETHODIMP nsFormFrame::GetPlatformEncoder(nsIUnicodeEncoder** encoder)
{
  *encoder = nsnull;
  nsAutoString localeCharset;
  nsresult rv = NS_OK;
  
  // Get Charset, get the encoder.
  nsICharsetConverterManager * ccm = nsnull;
  rv = nsServiceManager::GetService(kCharsetConverterManagerCID ,
                                    NS_GET_IID(nsICharsetConverterManager),
                                    (nsISupports**)&ccm);

  if(NS_SUCCEEDED(rv) && (nsnull != ccm)) {

     nsCOMPtr <nsIPlatformCharset> platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);

     if (NS_SUCCEEDED(rv)) {
        rv = platformCharset->GetCharset(kPlatformCharsetSel_FileName, localeCharset);
     }

     if (NS_FAILED(rv)) {
       NS_ASSERTION(0, "error getting locale charset, using ISO-8859-1");
       localeCharset.AssignWithConversion("ISO-8859-1");
       rv = NS_OK;
     }

     // get unicode converter mgr
     //NS_WITH_SERVICE(nsICharsetConverterManager, ccm, kCharsetConverterManagerCID, &rv); 

     if (NS_SUCCEEDED(rv)) {
       rv = ccm->GetUnicodeEncoder(&localeCharset, encoder);
     }
   } 

  return NS_OK;
}


#define CRLF "\015\012"   
nsresult nsFormFrame::ProcessAsURLEncoded(nsIFormProcessor* aFormProcessor, PRBool isPost, nsString& aData, nsIFormControlFrame* aFrame)
{
  nsresult rv = NS_OK;
  nsString buf;
  PRBool firstTime = PR_TRUE;
  PRUint32 numChildren = mFormControls.Count();

  nsCOMPtr<nsIUnicodeEncoder> encoder;
  if(NS_FAILED(GetEncoder(getter_AddRefs(encoder))))  // Non-fatal error
     encoder = nsnull;

  // collect and encode the data from the children controls
  for (PRUint32 childX = 0; childX < numChildren; childX++) {
    nsIFormControlFrame* child = (nsIFormControlFrame*) mFormControls.ElementAt(childX);
    if (child && child->IsSuccessful(aFrame)) {
      PRInt32 numValues = 0;
      PRInt32 maxNumValues = child->GetMaxNumValues();
      if (0 >= maxNumValues) {
        continue;
      }
      nsString* names = new nsString[maxNumValues];
      if (!names) {
        rv = NS_ERROR_OUT_OF_MEMORY;
      } else {
        nsString* values = new nsString[maxNumValues];
        if (!values) {
          rv = NS_ERROR_OUT_OF_MEMORY;
        } else {
          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.AppendWithConversion("&");
              }
              nsString* convName = URLEncode(names[valueX], encoder);
              buf += *convName;
              delete convName;
              buf.AppendWithConversion("=");
              nsAutoString newValue;
              newValue.Append(values[valueX]);
              if (aFormProcessor) {
                ProcessValue(*aFormProcessor, child, names[valueX], newValue);
              }
              nsString* convValue = URLEncode(newValue, encoder);
              buf += *convValue;
              delete convValue;
            }
          }
          delete [] values;
        }
        delete [] names;
      }
    }
  }
    aData.SetLength(0);
    if (isPost) {
      char size[16];
      sprintf(size, "%d", buf.Length());
      aData.AssignWithConversion("Content-type: application/x-www-form-urlencoded");
#ifdef SPECIFY_CHARSET_IN_CONTENT_TYPE
      nsString charset;
      GetSubmitCharset(charset);
      aData += "; charset=";
      aData += charset;
#endif
      aData.AppendWithConversion(CRLF);
      aData.AppendWithConversion("Content-Length: ");
      aData.AppendWithConversion(size);
      aData.AppendWithConversion(CRLF);
      aData.AppendWithConversion(CRLF);
    } 
  aData += buf;
  // Need to append CRLF to end of stream for compatability with Nav and IE
  if (isPost) {
    aData.AppendWithConversion(CRLF);
  }
  return rv;
}

// include the file name without the directory
const char*
nsFormFrame::GetFileNameWithinPath(char* aPathName)
{
#ifdef XP_MAC
  // On a Mac the only invalid character in a file name is a : so we have to avoid
  // the test for '\'
  char* fileNameStart = PL_strrchr(aPathName, ':');
#else
  char* fileNameStart = PL_strrchr(aPathName, '\\'); // windows
  if (!fileNameStart) { // try unix
    fileNameStart = PL_strrchr(aPathName, '/');
  }
#endif
  if (fileNameStart) { 
    return fileNameStart+1;
  }
  else {
    return aPathName;
  }
}

nsresult
nsFormFrame::GetContentType(char* aPathName, char** aContentType)
{
  nsresult rv = NS_OK;
  NS_ASSERTION(aContentType, "null pointer");

  if (aPathName && *aPathName) {
    // Get file extension and mimetype from that.g936
    char* fileExt = &aPathName[nsCRT::strlen(aPathName)-1];
    while (fileExt && (*fileExt != '.')) {
      fileExt--;
    }
    if (fileExt) {
      nsCOMPtr<nsIMIMEService> MIMEService (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
      if (NS_FAILED(rv)) return rv;
      if (NS_SUCCEEDED(MIMEService->GetTypeFromExtension(++fileExt, aContentType)))
          return NS_OK;
    }
  }
  *aContentType = nsCRT::strdup("application/octet-stream");
  if (!*aContentType) return NS_ERROR_OUT_OF_MEMORY;
  return NS_OK;
}

#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 SEP "--"

nsresult nsFormFrame::ProcessAsMultipart(nsIFormProcessor* aFormProcessor,nsIFileSpec*& aMultipartDataFile, nsIFormControlFrame* aFrame)
{
  char buffer[BUFSIZE];
  PRInt32 numChildren = mFormControls.Count();

  // Create a temporary file to write the form post data to
  nsSpecialSystemDirectory tempDir(nsSpecialSystemDirectory::OS_TemporaryDirectory);
  tempDir += "formpost";
  tempDir.MakeUnique();
  nsIFileSpec* postDataFile = nsnull;
  nsresult rv = NS_NewFileSpecWithSpec(tempDir, &postDataFile);
  NS_ASSERTION(NS_SUCCEEDED(rv), "Post data file couldn't be created!");
  if (NS_FAILED(rv)) return rv;

  // write the content-type, boundary to the tmp file
  char boundary[80];
  sprintf(boundary, "---------------------------%d%d%d", 
          rand(), rand(), rand());
  sprintf(buffer, "Content-type: %s; boundary=%s" CRLF, MULTIPART, boundary);
  PRInt32 wantbytes = 0, gotbytes = 0;
  rv = postDataFile->Write(buffer, wantbytes = PL_strlen(buffer), &gotbytes);
  if (NS_FAILED(rv) || (wantbytes != gotbytes)) return rv;

  nsCOMPtr<nsIUnicodeEncoder> encoder;
  if(NS_FAILED( GetEncoder(getter_AddRefs(encoder))))  // Non-fatal error
     encoder = nsnull;

  nsCOMPtr<nsIUnicodeEncoder> platformencoder;
  if(NS_FAILED(GetPlatformEncoder(getter_AddRefs(platformencoder))))  // Non-fatal error
     platformencoder = nsnull;


  PRInt32 boundaryLen = PL_strlen(boundary);
  PRInt32 contDispLen = PL_strlen(CONTENT_DISP);
  PRInt32 crlfLen     = PL_strlen(CRLF);
  PRInt32 sepLen      = PL_strlen(SEP);

  // compute the content length
  /////////////////////////////

  PRInt32 contentLen = 0; // extra crlf after content-length header not counted
  PRInt32 childX;  // stupid compiler
  for (childX = 0; childX < numChildren; childX++) {
    nsIFormControlFrame* child = (nsIFormControlFrame*) mFormControls.ElementAt(childX);
    if (child) {
      PRInt32 type;
      child->GetType(&type);
      if (child->IsSuccessful(aFrame)) {
        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 = nsnull;
          char* value = nsnull;

          nsString valueStr = values[valueX];
          if (aFormProcessor) {
            ProcessValue(*aFormProcessor, child, names[valueX], valueStr);
          }

          if(encoder) {
              name  = UnicodeToNewBytes(names[valueX].GetUnicode(), names[valueX].Length(), encoder);
          }
   
          //use the platformencoder only for values containing file names 
          if (NS_FORM_INPUT_FILE == type) { 
            if(platformencoder) {
                value  = UnicodeToNewBytes(valueStr.GetUnicode(), valueStr.Length(), platformencoder);
            }
          } else { 
            if(encoder) {
                value  = UnicodeToNewBytes(valueStr.GetUnicode(), valueStr.Length(), encoder);
            }
          } 

          if(nsnull == name)
            name  = names[valueX].ToNewCString();
          if(nsnull == value)
            value = valueStr.ToNewCString();

          if (0 == names[valueX].Length()) {
            continue;
          }

          // convert value to CRLF line breaks
          char* newValue = nsLinebreakConverter::ConvertLineBreaks(value,
                           nsLinebreakConverter::eLinebreakPlatform, nsLinebreakConverter::eLinebreakNet);
          delete [] value;
          value = newValue;
          
          // Add boundary line
          contentLen += sepLen + boundaryLen + crlfLen;

          // Add Content-Disp line
          contentLen += contDispLen;
          contentLen += PL_strlen(name);

          // File inputs also list filename on Content-Disp line
          if (NS_FORM_INPUT_FILE == type) { 
            contentLen += PL_strlen(FILENAME);
            const char* fileNameStart = GetFileNameWithinPath(value);
	          contentLen += PL_strlen(fileNameStart);
          }
          // End Content-Disp Line (quote plus CRLF)
          contentLen += 1 + crlfLen;  // ending name quote plus CRLF

          // File inputs add Content-Type line
          if (NS_FORM_INPUT_FILE == type) {
            char* contentType = nsnull;
            rv = GetContentType(value, &contentType);
            if (NS_FAILED(rv)) break; // Need to free up anything here?
            contentLen += PL_strlen(CONTENT_TYPE);
            contentLen += PL_strlen(contentType) + crlfLen;
            nsCRT::free(contentType);
          }
	  
          // Blank line before value
          contentLen += crlfLen;

          // File inputs add file contents next
          if (NS_FORM_INPUT_FILE == type) {
            do {
              // Because we have a native path to the file we can't use PR_GetFileInfo
              // on the Mac as it expects a Unix style path.  Instead we'll use our
              // spiffy new nsILocalFile
              nsILocalFile* tempFile = nsnull;
              rv = NS_NewLocalFile(value, PR_TRUE, &tempFile);
              NS_ASSERTION(tempFile, "Couldn't create nsIFileSpec to get file size!");
              if (NS_FAILED(rv) || !tempFile)
                break; // NS_ERROR_OUT_OF_MEMORY
              PRUint32 fileSize32 = 0;
              PRInt64  fileSize = LL_Zero();
              rv = tempFile->GetFileSize(&fileSize);
              if (NS_FAILED(rv)) {
                NS_RELEASE(tempFile);
                break;
              }
              LL_L2UI(fileSize32, fileSize);
              contentLen += fileSize32;
              NS_RELEASE(tempFile);
            } while (PR_FALSE);

            // Add CRLF after file
            contentLen += crlfLen;
          } else {
            // Non-file inputs add value line
            contentLen += PL_strlen(value) + crlfLen;
          }
          delete [] name;
          nsMemory::Free(value);
        }
        delete [] names;
        delete [] values;
      }

      aMultipartDataFile = postDataFile;
    }
  }

  // Add the post file boundary line
  contentLen += sepLen + boundaryLen + sepLen + crlfLen;

  // write the content 
  ////////////////////

  sprintf(buffer, "Content-Length: %d" CRLF CRLF, contentLen);
  rv = postDataFile->Write(buffer, wantbytes = PL_strlen(buffer), &gotbytes);
  if (NS_SUCCEEDED(rv) && (wantbytes == gotbytes)) {

    // write the content passing through all of the form controls a 2nd time
    for (childX = 0; childX < numChildren; childX++) {
      nsIFormControlFrame* child = (nsIFormControlFrame*) mFormControls.ElementAt(childX);
      if (child) {
        PRInt32 type;
        child->GetType(&type);
        if (child->IsSuccessful(aFrame)) {
          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 = nsnull;
            char* value = nsnull;

            if(encoder) {
              name  = UnicodeToNewBytes(names[valueX].GetUnicode(), names[valueX].Length(), encoder);
            }

            //use the platformencoder only for values containing file names 
            if (NS_FORM_INPUT_FILE == type) { 
              if(platformencoder) {
                  value = UnicodeToNewBytes(values[valueX].GetUnicode(), values[valueX].Length(), platformencoder);
              }
            } else { 
              if(encoder) {
                  value = UnicodeToNewBytes(values[valueX].GetUnicode(), values[valueX].Length(), encoder);
              }
            } 

            if(nsnull == name)
              name  = names[valueX].ToNewCString();
            if(nsnull == value)
              value = values[valueX].ToNewCString();

            if (0 == names[valueX].Length()) {
              continue;
            }

            // convert value to CRLF line breaks
            char* newValue = nsLinebreakConverter::ConvertLineBreaks(value,
                             nsLinebreakConverter::eLinebreakPlatform, nsLinebreakConverter::eLinebreakNet);
            delete [] value;
            value = newValue;

       	    // Print boundary line
            sprintf(buffer, SEP "%s" CRLF, boundary);
            rv = postDataFile->Write(buffer, wantbytes = PL_strlen(buffer), &gotbytes);
            if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;

            // Print Content-Disp line
            rv = postDataFile->Write(CONTENT_DISP, wantbytes = contDispLen, &gotbytes);
            if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;	  
            rv = postDataFile->Write(name, wantbytes = PL_strlen(name), &gotbytes);
            if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;

            // File inputs also list filename on Content-Disp line
            if (NS_FORM_INPUT_FILE == type) {
              rv = postDataFile->Write(FILENAME, wantbytes = PL_strlen(FILENAME), &gotbytes);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
              const char* fileNameStart = GetFileNameWithinPath(value);
              rv = postDataFile->Write(fileNameStart, wantbytes = PL_strlen(fileNameStart), &gotbytes);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
            }

            // End Content Disp
            rv = postDataFile->Write("\"" CRLF , wantbytes = PL_strlen("\"" CRLF), &gotbytes);
            if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;

            // File inputs write Content-Type line
            if (NS_FORM_INPUT_FILE == type) {
              char* contentType = nsnull;
              rv = GetContentType(value, &contentType);
              if (NS_FAILED(rv)) break;
              rv = postDataFile->Write(CONTENT_TYPE, wantbytes = PL_strlen(CONTENT_TYPE), &gotbytes);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
              rv = postDataFile->Write(contentType, wantbytes = PL_strlen(contentType), &gotbytes);
              nsCRT::free(contentType);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
              rv = postDataFile->Write(CRLF, wantbytes = PL_strlen(CRLF), &gotbytes);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
              // end content-type header
	          }

            // Blank line before value
            rv = postDataFile->Write(CRLF, wantbytes = PL_strlen(CRLF), &gotbytes);
            if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;

            // File inputs print file contents next
            if (NS_FORM_INPUT_FILE == type) {
              nsIFileSpec* contentFile = nsnull;
              rv = NS_NewFileSpec(&contentFile);
              NS_ASSERTION(contentFile, "Post content file couldn't be created!");
              if (NS_FAILED(rv) || !contentFile) break; // NS_ERROR_OUT_OF_MEMORY
              rv = contentFile->SetNativePath(value);
              NS_ASSERTION(contentFile, "Post content file path couldn't be set!");
              if (NS_FAILED(rv)) {
                NS_RELEASE(contentFile);
                break;
              }
              // Print file contents
              PRInt32 size = 1;
              while (1) {
                char* readbuffer = nsnull;
                rv = contentFile->Read(&readbuffer, BUFSIZE, &size);
                if (NS_FAILED(rv) || 0 >= size) break;
                rv = postDataFile->Write(readbuffer, wantbytes = size, &gotbytes);
                if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
              }
              NS_RELEASE(contentFile);
              // Print CRLF after file
              rv = postDataFile->Write(CRLF, wantbytes = PL_strlen(CRLF), &gotbytes);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
            }

            // Non-file inputs print value line
            else {
              rv = postDataFile->Write(value, wantbytes = PL_strlen(value), &gotbytes);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
              rv = postDataFile->Write(CRLF, wantbytes = PL_strlen(CRLF), &gotbytes);
              if (NS_FAILED(rv) || (wantbytes != gotbytes)) break;
            }
            delete [] name;
            nsMemory::Free(value);
          }
          delete [] names;
          delete [] values;
        }
      }
    }
  }

  if (NS_SUCCEEDED(rv)) {
    sprintf(buffer, SEP "%s" SEP CRLF, boundary);
    rv = postDataFile->Write(buffer, wantbytes = PL_strlen(buffer), &gotbytes);
    if (NS_SUCCEEDED(rv) && (wantbytes == gotbytes)) {
      rv = postDataFile->CloseStream();
    }
  }

  NS_ASSERTION(NS_SUCCEEDED(rv), "Generating the form post temp file failed.\n");
  return rv;
}

// static helper functions for nsIFormControls

PRBool
nsFormFrame::GetDisabled(nsIFrame* aChildFrame, nsIContent* aContent) 
{
  PRBool result = PR_FALSE;

  nsIContent* content = aContent;
  if (nsnull == content) {
    aChildFrame->GetContent(&content);
  }
  if (nsnull != content) {
    nsIHTMLContent* htmlContent = nsnull;
    content->QueryInterface(kIHTMLContentIID, (void**)&htmlContent);
    if (nsnull != htmlContent) {
      nsHTMLValue value;
      if (NS_CONTENT_ATTR_HAS_VALUE == htmlContent->GetHTMLAttribute(nsHTMLAtoms::disabled, value)) {
        result = PR_TRUE;
      }
      NS_RELEASE(htmlContent);
    }
    if (nsnull == aContent) {
      NS_RELEASE(content);
    }
  }
  return result;
}

PRBool
nsFormFrame::GetReadonly(nsIFrame* aChildFrame, nsIContent* aContent) 
{
  PRBool result = PR_FALSE;

  nsIContent* content = aContent;
  if (nsnull == content) {
    aChildFrame->GetContent(&content);
  }
  if (nsnull != content) {
    nsIHTMLContent* htmlContent = nsnull;
    content->QueryInterface(kIHTMLContentIID, (void**)&htmlContent);
    if (nsnull != htmlContent) {
      nsHTMLValue value;
      if (NS_CONTENT_ATTR_HAS_VALUE == htmlContent->GetHTMLAttribute(nsHTMLAtoms::readonly, value)) {
        result = PR_TRUE;
      }
      NS_RELEASE(htmlContent);
    }
    if (nsnull == aContent) {
      NS_RELEASE(content);
    }
  }
  return result;
}

nsresult
nsFormFrame::GetName(nsIFrame* aChildFrame, nsString& aName, nsIContent* aContent) 
{
  nsresult result = NS_FORM_NOTOK;

  nsIContent* content = aContent;
  if (nsnull == content) {
    aChildFrame->GetContent(&content);
  }
  if (nsnull != content) {
    nsIHTMLContent* htmlContent = nsnull;
    result = content->QueryInterface(kIHTMLContentIID, (void**)&htmlContent);
    if (NS_SUCCEEDED(result) && (nsnull != htmlContent)) {
      nsHTMLValue value;
      result = htmlContent->GetHTMLAttribute(nsHTMLAtoms::name, value);
      if (NS_CONTENT_ATTR_HAS_VALUE == result) {
        if (eHTMLUnit_String == value.GetUnit()) {
          value.GetStringValue(aName);
        }
      }
      NS_RELEASE(htmlContent);
    }
    if (nsnull == aContent) {
      NS_RELEASE(content);
    }
  }
  return result;
}

nsresult
nsFormFrame::GetValue(nsIFrame* aChildFrame, nsString& aValue, nsIContent* aContent) 
{
  nsresult result = NS_FORM_NOTOK;

  nsIContent* content = aContent;
  if (nsnull == content) {
    aChildFrame->GetContent(&content);
  }
  if (nsnull != content) {
    nsIHTMLContent* htmlContent = nsnull;
    result = content->QueryInterface(kIHTMLContentIID, (void**)&htmlContent);
    if (NS_SUCCEEDED(result) && (nsnull != htmlContent)) {
      nsHTMLValue value;
      result = htmlContent->GetHTMLAttribute(nsHTMLAtoms::value, value);
      if (NS_CONTENT_ATTR_HAS_VALUE == result) {
        if (eHTMLUnit_String == value.GetUnit()) {
          value.GetStringValue(aValue);
        }
      }
      NS_RELEASE(htmlContent);
    }
    if (nsnull == aContent) {
      NS_RELEASE(content);
    }
  }
  return result;
}

void
nsFormFrame::StyleChangeReflow(nsIPresContext* aPresContext,
                               nsIFrame* aFrame)
{
  nsCOMPtr<nsIPresShell> shell;
  aPresContext->GetShell(getter_AddRefs(shell));
    
  nsIReflowCommand* reflowCmd;
  nsresult rv = NS_NewHTMLReflowCommand(&reflowCmd, aFrame,
                                        nsIReflowCommand::StyleChanged);
  if (NS_SUCCEEDED(rv)) {
    shell->AppendReflowCommand(reflowCmd);
    NS_RELEASE(reflowCmd);
  }
}
