Bug 546528 part 3: Implement FormData. r=benjamn
authorJonas Sicking <jonas@sicking.cc>
Wed, 24 Feb 2010 21:58:18 -0800
changeset 38679 00bc3f1670403a528993a4e46460241529cb54e1
parent 38678 e3cf67c96fedd8d1317effbe6670c6404ed0a4b9
child 38680 241ac713765039192e12e381e322ac98526ad5cb
push id11798
push usersicking@mozilla.com
push dateThu, 25 Feb 2010 06:06:03 +0000
treeherderautoland@00bc3f167040 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbenjamn
bugs546528
milestone1.9.3a2pre
Bug 546528 part 3: Implement FormData. r=benjamn
content/base/public/Makefile.in
content/base/public/nsIDOMFormData.idl
content/base/public/nsIXMLHttpRequest.idl
content/base/src/Makefile.in
content/base/src/nsFormData.cpp
content/base/src/nsFormData.h
content/base/src/nsXMLHttpRequest.cpp
content/html/content/public/nsIFormSubmission.h
content/html/content/src/nsFormSubmission.cpp
content/html/content/src/nsHTMLFormElement.cpp
content/html/content/test/test_formSubmission.html
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoID.h
dom/interfaces/html/nsIDOMNSHTMLFormElement.idl
layout/build/nsLayoutModule.cpp
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -92,16 +92,17 @@ XPIDLSRCS	= \
 		nsIContentPolicy.idl        \
 		nsIDocumentEncoder.idl      \
 		nsIDOMFile.idl \
 		nsIDOMFileReader.idl \
 		nsIDOMFileInternal.idl \
 		nsIDOMFileList.idl \
 		nsIDOMFileException.idl \
 		nsIDOMFileError.idl \
+		nsIDOMFormData.idl \
 		nsIDOMParser.idl \
 		nsIDOMSerializer.idl \
 		nsISelection2.idl \
 		nsISelectionController.idl  \
 		nsISelectionDisplay.idl  \
 		nsISelectionListener.idl  \
 		nsISelectionPrivate.idl  \
 		nsIScriptLoaderObserver.idl  \
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsIDOMFormData.idl
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(256c9139-5a29-41e1-8698-f9f9aae7d6cf)]
+interface nsIDOMFormData : nsISupports 
+{
+  void append(in DOMString name, in nsIVariant value);
+};
+
+%{ C++
+#define NS_FORMDATA_CID \
+{ 0x6b192618, 0x26f2, 0x426e, \
+  { 0xa7, 0xac, 0x1e, 0x13, 0xa6, 0xa4, 0x52, 0x2b } }
+
+#define NS_FORMDATA_CONTRACTID "@mozilla.org/files/formdata;1"
+%}
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -40,16 +40,17 @@
 interface nsIChannel;
 interface nsIDOMDocument;
 interface nsIDOMEventListener;
 interface nsIPrincipal;
 interface nsIScriptContext;
 interface nsIURI;
 interface nsIVariant;
 interface nsPIDOMWindow;
+interface nsIInputStream;
 
 [scriptable, uuid(6ce0a193-b033-4c3d-b748-f851b09261f5)]
 interface nsIXMLHttpRequestEventTarget : nsIDOMEventTarget {
   // event handler attributes
   attribute nsIDOMEventListener onabort;
   attribute nsIDOMEventListener onerror;
   attribute nsIDOMEventListener onload;
   attribute nsIDOMEventListener onloadstart;
@@ -366,16 +367,23 @@ interface nsIXMLHttpRequest : nsISupport
    * After the initial response, all event listeners will be cleared.
    * // XXXbz what does that mean, exactly?   
    *
    * Call open() before setting an onreadystatechange listener.
    */
   attribute nsIDOMEventListener onreadystatechange;
 };
 
+[scriptable, uuid(840d0d00-e83e-4a29-b3c7-67e96e90a499)]
+interface nsIXHRSendable : nsISupports {
+  void getSendInfo(out nsIInputStream body,
+                   out ACString contentType,
+                   out ACString charset);
+};
+
 /**
  * DEPRECATED.
  */
 [scriptable, uuid(423fdd3d-41c9-4149-8fe5-b14a1d3912a0)]
 interface nsIJSXMLHttpRequest : nsISupports {
   /**
    * Meant to be a script-only mechanism for setting an upload progress event
    * listener.
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -95,16 +95,17 @@ CPPSRCS		= \
 		nsDOMLists.cpp \
 		nsDOMParser.cpp \
 		nsDOMSerializer.cpp \
 		nsDOMTokenList.cpp \
 		nsDocument.cpp \
 		nsDocumentEncoder.cpp \
 		nsDocumentFragment.cpp \
 		nsFrameLoader.cpp \
+		nsFormData.cpp \
 		nsGenConImageContent.cpp \
 		nsGenericDOMDataNode.cpp \
 		nsGenericElement.cpp \
 		nsGkAtoms.cpp \
 		nsHTMLContentSerializer.cpp \
 		nsImageLoadingContent.cpp \
 		nsLineBreaker.cpp \
 		nsLoadListenerProxy.cpp \
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsFormData.cpp
@@ -0,0 +1,158 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * mozilla.org.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsFormData.h"
+#include "nsIVariant.h"
+#include "nsIDOMFileInternal.h"
+#include "nsIInputStream.h"
+#include "nsIFile.h"
+#include "nsContentUtils.h"
+
+nsFormData::nsFormData()
+  : nsFormSubmission(NS_LITERAL_CSTRING("UTF-8"))
+{
+}
+
+// -------------------------------------------------------------------------
+// nsISupports
+
+NS_IMPL_ADDREF(nsFormData)
+NS_IMPL_RELEASE(nsFormData)
+NS_INTERFACE_MAP_BEGIN(nsFormData)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMFormData)
+  NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
+  NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(FormData)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormData)
+NS_INTERFACE_MAP_END
+
+// -------------------------------------------------------------------------
+// nsFormSubmission
+nsresult
+nsFormData::GetEncodedSubmission(nsIURI* aURI,
+                                 nsIInputStream** aPostDataStream)
+{
+  NS_NOTREACHED("Shouldn't call nsFormData::GetEncodedSubmission");
+  return NS_OK;
+}
+
+nsresult
+nsFormData::AddNameValuePair(const nsAString& aName,
+                             const nsAString& aValue)
+{
+  FormDataTuple* data = mFormData.AppendElement();
+  data->name = aName;
+  data->stringValue = aValue;
+  data->valueIsFile = PR_FALSE;
+
+  return NS_OK;
+}
+
+nsresult
+nsFormData::AddNameFilePair(const nsAString& aName,
+                            nsIFile* aFile)
+{
+  FormDataTuple* data = mFormData.AppendElement();
+  data->name = aName;
+  data->fileValue = aFile;
+  data->valueIsFile = PR_TRUE;
+
+  return NS_OK;
+}
+
+// -------------------------------------------------------------------------
+// nsIDOMFormData
+
+NS_IMETHODIMP
+nsFormData::Append(const nsAString& aName, nsIVariant* aValue)
+{
+  PRUint16 dataType;
+  nsresult rv = aValue->GetDataType(&dataType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (dataType == nsIDataType::VTYPE_INTERFACE ||
+      dataType == nsIDataType::VTYPE_INTERFACE_IS) {
+    nsCOMPtr<nsISupports> supports;
+    nsID *iid;
+    rv = aValue->GetAsInterface(&iid, getter_AddRefs(supports));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsMemory::Free(iid);
+
+    nsCOMPtr<nsIDOMFileInternal> domFile = do_QueryInterface(supports);
+    if (domFile) {
+      nsCOMPtr<nsIFile> file;
+      rv = domFile->GetInternalFile(getter_AddRefs(file));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      return AddNameFilePair(aName, file);
+    }
+  }
+
+  PRUnichar* stringData = nsnull;
+  PRUint32 stringLen = 0;
+  rv = aValue->GetAsWStringWithSize(&stringLen, &stringData);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString valAsString;
+  valAsString.Adopt(stringData, stringLen);
+
+  return AddNameValuePair(aName, valAsString);
+}
+
+// -------------------------------------------------------------------------
+// nsIDXHRSendable
+
+NS_IMETHODIMP
+nsFormData::GetSendInfo(nsIInputStream** aBody, nsACString& aContentType,
+                        nsACString& aCharset)
+{
+  nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"));
+  
+  for (PRUint32 i = 0; i < mFormData.Length(); ++i) {
+    if (mFormData[i].valueIsFile) {
+      fs.AddNameFilePair(mFormData[i].name, mFormData[i].fileValue);
+    }
+    else {
+      fs.AddNameValuePair(mFormData[i].name, mFormData[i].stringValue);
+    }
+  }
+
+  fs.GetContentType(aContentType);
+  aCharset.Truncate();
+  NS_ADDREF(*aBody = fs.GetSubmissionBody());
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsFormData.h
@@ -0,0 +1,78 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * mozilla.org.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsFormData_h__
+#define nsFormData_h__
+
+#include "nsIDOMFormData.h"
+#include "nsIXMLHttpRequest.h"
+#include "nsIFormSubmission.h"
+#include "nsTArray.h"
+
+class nsFormData : public nsIDOMFormData,
+                   public nsIXHRSendable,
+                   public nsFormSubmission
+{
+public:
+  nsFormData();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMFORMDATA
+  NS_DECL_NSIXHRSENDABLE
+
+  // nsFormSubmission
+  virtual nsresult GetEncodedSubmission(nsIURI* aURI,
+                                        nsIInputStream** aPostDataStream);
+  virtual nsresult AddNameValuePair(const nsAString& aName,
+                                    const nsAString& aValue);
+  virtual nsresult AddNameFilePair(const nsAString& aName,
+                                   nsIFile* aFile);
+
+  
+
+private:
+  struct FormDataTuple
+  {
+    nsString name;
+    nsString stringValue;
+    nsCOMPtr<nsIFile> fileValue;
+    PRBool valueIsFile;
+  };
+  
+  nsTArray<FormDataTuple> mFormData;
+};
+
+#endif // nsFormData_h__
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2294,16 +2294,22 @@ GetRequestBody(nsIVariant* aBody, nsIInp
       rv = mimeService->GetTypeFromFile(internalFile, aContentType);
       if (NS_FAILED(rv)) {
         aContentType.Truncate();
       }
 
       // Feed local file input stream into our upload channel
       return NS_NewLocalFileInputStream(aResult, internalFile);
     }
+
+    // nsIXHRSendable?
+    nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
+    if (sendable) {
+      return sendable->GetSendInfo(aResult, aContentType, aCharset);
+    }
   }
   else if (dataType == nsIDataType::VTYPE_VOID ||
            dataType == nsIDataType::VTYPE_EMPTY) {
     // Makes us act as if !aBody, don't upload anything
     return NS_OK;
   }
 
   PRUnichar* data = nsnull;
--- a/content/html/content/public/nsIFormSubmission.h
+++ b/content/html/content/public/nsIFormSubmission.h
@@ -46,16 +46,17 @@ class nsIInputStream;
 class nsGenericHTMLElement;
 class nsILinkHandler;
 class nsIContent;
 class nsIFormControl;
 class nsIDOMHTMLElement;
 class nsIDocShell;
 class nsIRequest;
 class nsISaveAsCharset;
+class nsIMultiplexInputStream;
 
 /**
  * Class for form submissions; encompasses the function to call to submit as
  * well as the form submission name/value pairs
  */
 class nsFormSubmission
 {
 public:
@@ -133,16 +134,76 @@ public:
   nsresult EncodeVal(const nsAString& aStr, nsACString& aResult);
 
 private:
   // The encoder that will encode Unicode names and values
   nsCOMPtr<nsISaveAsCharset> mEncoder;
 };
 
 /**
+ * Handle multipart/form-data encoding, which does files as well as normal
+ * inputs.  This always does POST.
+ */
+class nsFSMultipartFormData : public nsEncodingFormSubmission
+{
+public:
+  /**
+   * @param aCharset the charset of the form as a string
+   */
+  nsFSMultipartFormData(const nsACString& aCharset);
+  ~nsFSMultipartFormData();
+ 
+  virtual nsresult AddNameValuePair(const nsAString& aName,
+                                    const nsAString& aValue);
+  virtual nsresult AddNameFilePair(const nsAString& aName,
+                                   nsIFile* aFile);
+  virtual nsresult GetEncodedSubmission(nsIURI* aURI,
+                                        nsIInputStream** aPostDataStream);
+
+  void GetContentType(nsACString& aContentType)
+  {
+    aContentType =
+      NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary;
+  }
+
+  nsIInputStream* GetSubmissionBody();
+
+protected:
+
+  /**
+   * Roll up the data we have so far and add it to the multiplexed data stream.
+   */
+  nsresult AddPostDataStream();
+
+private:
+  /**
+   * The post data stream as it is so far.  This is a collection of smaller
+   * chunks--string streams and file streams interleaved to make one big POST
+   * stream.
+   */
+  nsCOMPtr<nsIMultiplexInputStream> mPostDataStream;
+
+  /**
+   * The current string chunk.  When a file is hit, the string chunk gets
+   * wrapped up into an input stream and put into mPostDataStream so that the
+   * file input stream can then be appended and everything is in the right
+   * order.  Then the string chunk gets appended to again as we process more
+   * name/value pairs.
+   */
+  nsCString mPostDataChunk;
+
+  /**
+   * The boundary string to use after each "part" (the boundary that marks the
+   * end of a value).  This is computed randomly and is different for each
+   * submission.
+   */
+  nsCString mBoundary;
+};
+
+/**
  * Get a submission object based on attributes in the form (ENCTYPE and METHOD)
  *
  * @param aForm the form to get a submission object based on
  * @param aFormSubmission the form submission object (out param)
  */
 nsresult GetSubmissionFromForm(nsGenericHTMLElement* aForm,
                                nsFormSubmission** aFormSubmission);
 
--- a/content/html/content/src/nsFormSubmission.cpp
+++ b/content/html/content/src/nsFormSubmission.cpp
@@ -374,80 +374,46 @@ nsFSURLEncoded::URLEncode(const nsAStrin
   NS_ENSURE_TRUE(escapedBuf, NS_ERROR_OUT_OF_MEMORY);
   aEncoded.Adopt(escapedBuf);
 
   return NS_OK;
 }
 
 // --------------------------------------------------------------------------
 
-/**
- * Handle multipart/form-data encoding, which does files as well as normal
- * inputs.  This always does POST.
- */
-class nsFSMultipartFormData : public nsEncodingFormSubmission
-{
-public:
-  /**
-   * @param aCharset the charset of the form as a string
-   */
-  nsFSMultipartFormData(const nsACString& aCharset);
-  ~nsFSMultipartFormData();
- 
-  virtual nsresult AddNameValuePair(const nsAString& aName,
-                                    const nsAString& aValue);
-  virtual nsresult AddNameFilePair(const nsAString& aName,
-                                   nsIFile* aFile);
-  virtual nsresult GetEncodedSubmission(nsIURI* aURI,
-                                        nsIInputStream** aPostDataStream);
-
-protected:
-
-  /**
-   * Roll up the data we have so far and add it to the multiplexed data stream.
-   */
-  nsresult AddPostDataStream();
-
-private:
-  /**
-   * The post data stream as it is so far.  This is a collection of smaller
-   * chunks--string streams and file streams interleaved to make one big POST
-   * stream.
-   */
-  nsCOMPtr<nsIMultiplexInputStream> mPostDataStream;
-
-  /**
-   * The current string chunk.  When a file is hit, the string chunk gets
-   * wrapped up into an input stream and put into mPostDataStream so that the
-   * file input stream can then be appended and everything is in the right
-   * order.  Then the string chunk gets appended to again as we process more
-   * name/value pairs.
-   */
-  nsCString mPostDataChunk;
-
-  /**
-   * The boundary string to use after each "part" (the boundary that marks the
-   * end of a value).  This is computed randomly and is different for each
-   * submission.
-   */
-  nsCString mBoundary;
-};
-
 nsFSMultipartFormData::nsFSMultipartFormData(const nsACString& aCharset)
     : nsEncodingFormSubmission(aCharset)
 {
   mPostDataStream =
     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
 
   mBoundary.AssignLiteral("---------------------------");
   mBoundary.AppendInt(rand());
   mBoundary.AppendInt(rand());
   mBoundary.AppendInt(rand());
 }
 
+nsFSMultipartFormData::~nsFSMultipartFormData()
+{
+  NS_ASSERTION(mPostDataChunk.IsEmpty(), "Left unsubmitted data");
+}
+
+nsIInputStream*
+nsFSMultipartFormData::GetSubmissionBody()
+{
+  // Finish data
+  mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
+                  + NS_LITERAL_CSTRING("--" CRLF);
+
+  // Add final data input stream
+  AddPostDataStream();
+
+  return mPostDataStream;
+}
+
 nsresult
 nsFSMultipartFormData::AddNameValuePair(const nsAString& aName,
                                         const nsAString& aValue)
 {
   nsCString valueStr;
   nsCAutoString encodedVal;
   nsresult rv = EncodeVal(aValue, encodedVal);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -561,38 +527,28 @@ nsFSMultipartFormData::AddNameFilePair(c
 }
 
 nsresult
 nsFSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
                                             nsIInputStream** aPostDataStream)
 {
   nsresult rv;
 
-  // Finish data
-  mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
-                  + NS_LITERAL_CSTRING("--" CRLF);
-
-  // Add final data input stream
-  AddPostDataStream();
-
   // Make header
   nsCOMPtr<nsIMIMEInputStream> mimeStream
     = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCAutoString boundaryHeaderValue(
-    NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary);
-
-  mimeStream->AddHeader("Content-Type", boundaryHeaderValue.get());
+  nsCAutoString contentType;
+  GetContentType(contentType);
+  mimeStream->AddHeader("Content-Type", contentType.get());
   mimeStream->SetAddContentLength(PR_TRUE);
-  mimeStream->SetData(mPostDataStream);
+  mimeStream->SetData(GetSubmissionBody());
 
-  *aPostDataStream = mimeStream;
-
-  NS_ADDREF(*aPostDataStream);
+  *aPostDataStream = mimeStream.forget().get();
 
   return NS_OK;
 }
 
 nsresult
 nsFSMultipartFormData::AddPostDataStream()
 {
   nsresult rv = NS_OK;
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -61,16 +61,17 @@
 #include "nsICategoryManager.h"
 #include "nsCategoryManagerUtils.h"
 #include "nsISimpleEnumerator.h"
 #include "nsRange.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 #include "nsIWebProgress.h"
 #include "nsIDocShell.h"
+#include "nsFormData.h"
 
 // radio buttons
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIRadioControlElement.h"
 #include "nsIRadioVisitor.h"
 
 #include "nsLayoutUtils.h"
 
@@ -1486,16 +1487,29 @@ nsHTMLFormElement::GetEncoding(nsAString
   return GetEnctype(aEncoding);
 }
  
 NS_IMETHODIMP
 nsHTMLFormElement::SetEncoding(const nsAString& aEncoding)
 {
   return SetEnctype(aEncoding);
 }
+
+NS_IMETHODIMP
+nsHTMLFormElement::GetFormData(nsIDOMFormData** aFormData)
+{
+  nsRefPtr<nsFormData> fd = new nsFormData();
+
+  nsresult rv = WalkFormElements(fd, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aFormData = fd.forget().get();
+
+  return NS_OK;
+}
  
 NS_IMETHODIMP    
 nsHTMLFormElement::GetLength(PRInt32* aLength)
 {
   PRUint32 length;
   nsresult rv = mControls->GetLength(&length);
   *aLength = length;
   return rv;
--- a/content/html/content/test/test_formSubmission.html
+++ b/content/html/content/test/test_formSubmission.html
@@ -446,16 +446,27 @@ var expectedSub = [
   { name: "msel_23", value: "msel_23_v2" },
   { name: "msel_23", value: "msel_23_v3" },
   { name: "msel_26", value: "msel_26_v2" },
   { name: "msel_27", value: "msel_27_v2" },
   { name: "msel_29", value: "msel_29_v1" },
   { name: "msel_31", value: "msel_31_v1" },
 ];
 
+expectedAugment = [
+  { name: "aName", value: "aValue" },
+  //{ name: "aNameBool", value: "false" },
+  { name: "aNameNum", value: "9.2" },
+  { name: "aNameFile1", value: myFile1 },
+  { name: "aNameFile2", value: myFile2 },
+  //{ name: "aNameObj", value: "[object XMLHttpRequest]" },
+  //{ name: "aNameNull", value: "null" },
+  //{ name: "aNameUndef", value: "undefined" },
+];
+
 function checkMPSubmission(sub, expected) {
   function getPropCount(o) {
     var x, l = 0;
     for (x in o) ++l;
     return l;
   }
 
   is(sub.length, expected.length,
@@ -516,33 +527,35 @@ function runTest() {
   // Set up the expectedSub array
   fileReader1 = new FileReader;
   fileReader1.readAsBinaryString(myFile1);
   fileReader2 = new FileReader;
   fileReader2.readAsBinaryString(myFile2);
   fileReader1.onload = fileReader2.onload = function() { gen.next(); };
   yield; // Wait for both FileReaders. We don't care which order they finish.
   yield;
-  expectedSub.forEach(function(o) {
+  function fileFixup(o) {
     if (o.value == myFile1) {
       o.value = fileReader1.result;
       o.fileName = myFile1.name;
       o.contentType = myFile1.type;
     }
     else if (o.value == myFile2) {
       o.value = fileReader2.result;
       o.fileName = myFile2.name;
       o.contentType = myFile2.type;
     }
     else if (o.value == emptyFile) {
       o.value = "";
       o.fileName = emptyFile.name;
       o.contentType = emptyFile.type;
     }
-  });
+  };
+  expectedSub.forEach(fileFixup);
+  expectedAugment.forEach(fileFixup);
 
   // multipart/form-data
 
   // Make normal submission
   var form = $("form");
   var iframe = $("target_iframe");
   iframe.onload = function() { gen.next(); };
   form.submit();
@@ -586,16 +599,57 @@ function runTest() {
   form.action = "form_submit_server.sjs";
   form.method = "";
   form.enctype = "";
   form.submit();
   yield;
   submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
   checkPlainSubmission(submission, expectedSub);
 
+  // Send form using XHR and FormData
+  xhr = new XMLHttpRequest();
+  xhr.onload = function() { gen.next(); };
+  xhr.open("POST", "form_submit_server.sjs");
+  xhr.send(form.getFormData());
+  yield; // Wait for XHR load
+  checkMPSubmission(JSON.parse(xhr.responseText), expectedSub);
+  
+  // Send disabled form using XHR and FormData
+  setDisabled(document.getElementsByTagName("input"), true);
+  setDisabled(document.getElementsByTagName("select"), true);
+  xhr.open("POST", "form_submit_server.sjs");
+  xhr.send(form.getFormData());
+  yield;
+  checkMPSubmission(JSON.parse(xhr.responseText), []);
+  setDisabled(document.getElementsByTagName("input"), false);
+  setDisabled(document.getElementsByTagName("select"), false);
+  
+  // Send FormData
+  function addToFormData(fd) {
+    fd.append("aName", "aValue");
+    fd.append("aNameNum", 9.2);
+    fd.append("aNameFile1", myFile1);
+    fd.append("aNameFile2", myFile2);
+  }
+  var fd = new FormData();
+  addToFormData(fd);
+  xhr.open("POST", "form_submit_server.sjs");
+  xhr.send(fd);
+  yield;
+  checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment);
+
+  // Augment <form> using FormData
+  fd = form.getFormData();
+  addToFormData(fd);
+  xhr.open("POST", "form_submit_server.sjs");
+  xhr.send(fd);
+  yield;
+  checkMPSubmission(JSON.parse(xhr.responseText),
+                    expectedSub.concat(expectedAugment));
+
   SimpleTest.finish();
   yield;
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -453,16 +453,17 @@
 
 // Workers
 #include "nsDOMWorker.h"
 
 #include "nsDOMFile.h"
 #include "nsDOMFileReader.h"
 #include "nsIDOMFileException.h"
 #include "nsIDOMFileError.h"
+#include "nsIDOMFormData.h"
 
 // Simple gestures include
 #include "nsIDOMSimpleGestureEvent.h"
 
 #include "nsIDOMNSMouseEvent.h"
 
 #include "nsIEventListenerService.h"
 
@@ -1332,32 +1333,36 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(ScrollAreaEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(PopStateEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(EventListenerInfo, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(TransitionEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(FormData, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 };
 
 // Objects that should be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
 
 #define NS_DEFINE_CONSTRUCTOR_DATA(_class, _contract_id)                      \
   { eDOMClassInfo_##_class##_id, _contract_id },
 
 static const nsContractIDMapData kConstructorMap[] =
 {
   NS_DEFINE_CONSTRUCTOR_DATA(DOMParser, NS_DOMPARSER_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(FileReader, NS_FILEREADER_CONTRACTID)
+  NS_DEFINE_CONSTRUCTOR_DATA(FormData, NS_FORMDATA_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XMLSerializer, NS_XMLSERIALIZER_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XMLHttpRequest, NS_XMLHTTPREQUEST_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XPathEvaluator, NS_XPATH_EVALUATOR_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XSLTProcessor,
                              "@mozilla.org/document-transformer;1?type=xslt")
 };
 
 struct nsConstructorFuncMapData
@@ -3743,16 +3748,20 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIEventListenerInfo)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(TransitionEvent, nsIDOMTransitionEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMTransitionEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(FormData, nsIDOMFormData)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMFormData)
+  DOM_CLASSINFO_MAP_END
+
 #ifdef NS_DEBUG
   {
     PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/dom/base/nsDOMClassInfoID.h
+++ b/dom/base/nsDOMClassInfoID.h
@@ -474,16 +474,18 @@ enum nsDOMClassInfoID {
 
   eDOMClassInfo_ScrollAreaEvent_id,
   eDOMClassInfo_PopStateEvent_id,
 
   eDOMClassInfo_EventListenerInfo_id,
 
   eDOMClassInfo_TransitionEvent_id,
 
+  eDOMClassInfo_FormData_id,
+
   // This one better be the last one in this list
   eDOMClassInfoIDCount
 };
 
 /**
  * nsIClassInfo helper macros
  */
 
--- a/dom/interfaces/html/nsIDOMNSHTMLFormElement.idl
+++ b/dom/interfaces/html/nsIDOMNSHTMLFormElement.idl
@@ -34,13 +34,16 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
-[scriptable, uuid(a6cf90c6-15b3-11d2-932e-00805f8add32)]
+interface nsIDOMFormData;
+
+[scriptable, uuid(a5735b98-7a5f-4242-8c9a-3805f3f61b76)]
 interface nsIDOMNSHTMLFormElement : nsISupports
 {
            attribute DOMString        encoding;
+  nsIDOMFormData getFormData();
 };
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -112,16 +112,17 @@
 // view stuff
 #include "nsViewsCID.h"
 #include "nsViewManager.h"
 #include "nsContentCreatorFunctions.h"
 
 // DOM includes
 #include "nsDOMException.h"
 #include "nsDOMFileReader.h"
+#include "nsFormData.h"
 #include "nsGlobalWindowCommands.h"
 #include "nsIControllerCommandTable.h"
 #include "nsJSProtocolHandler.h"
 #include "nsScriptNameSpaceManager.h"
 #include "nsIControllerContext.h"
 #include "nsDOMScriptObjectFactory.h"
 #include "nsDOMStorage.h"
 #include "nsJSON.h"
@@ -286,16 +287,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsXPath1S
 
 // Factory Constructor
 NS_GENERIC_FACTORY_CONSTRUCTOR(txMozillaXSLTProcessor)
 NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(nsXPathEvaluator, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(txNodeSetAdaptor, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSerializer)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsXMLHttpRequest, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDOMFileReader, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
                                          nsDOMStorageManager::GetInstance)
 
 //-----------------------------------------------------------------------------
 
 // Per bug 209804, it is necessary to observe the "xpcom-shutdown" event and
 // perform shutdown of the layout modules at that time instead of waiting for
@@ -1422,16 +1424,21 @@ static const nsModuleComponentInfo gComp
     NS_XMLSERIALIZER_CONTRACTID,
     nsDOMSerializerConstructor },
 
   { "FileReader",
     NS_FILEREADER_CID,
     NS_FILEREADER_CONTRACTID,
     nsDOMFileReaderConstructor },
 
+  { "FormData",
+    NS_FORMDATA_CID,
+    NS_FORMDATA_CONTRACTID,
+    nsFormDataConstructor },
+
   { "XMLHttpRequest",
     NS_XMLHTTPREQUEST_CID,
     NS_XMLHTTPREQUEST_CONTRACTID,
     nsXMLHttpRequestConstructor },
 
   { "DOM Parser",
     NS_DOMPARSER_CID,
     NS_DOMPARSER_CONTRACTID,