Bug 1085283 - Patch 1 Implement FormData manipulation methods. r=baku
☠☠ backed out by 717e92977f9a ☠ ☠
authorNikhil Marathe <nsm.nikhil@gmail.com>
Wed, 28 Jan 2015 17:04:28 -0800
changeset 257301 ef51eb31fa09d3580a68d4c918646cde5a456662
parent 257300 886bc136c20b4db8570c75ed1b74b15615a5a492
child 257302 cc77a5165615f32a6d85087cc80d0bf88f93db9f
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1085283
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1085283 - Patch 1 Implement FormData manipulation methods. r=baku
dom/base/nsFormData.cpp
dom/base/nsFormData.h
dom/html/nsFormSubmission.cpp
dom/html/nsFormSubmission.h
dom/html/test/test_formData.html
dom/webidl/FormData.webidl
--- a/dom/base/nsFormData.cpp
+++ b/dom/base/nsFormData.cpp
@@ -1,19 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsFormData.h"
 #include "nsIVariant.h"
 #include "nsIInputStream.h"
 #include "mozilla/dom/File.h"
-#include "mozilla/dom/File.h"
 #include "mozilla/dom/HTMLFormElement.h"
-#include "mozilla/dom/FormDataBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsFormData::nsFormData(nsISupports* aOwner)
   : nsFormSubmission(NS_LITERAL_CSTRING("UTF-8"), nullptr)
   , mOwner(aOwner)
 {
@@ -81,16 +79,129 @@ nsFormData::Append(const nsAString& aNam
   if (aFilename.WasPassed()) {
     filename = aFilename.Value();
   } else {
     filename.SetIsVoid(true);
   }
   AddNameFilePair(aName, &aBlob, filename);
 }
 
+void
+nsFormData::Delete(const nsAString& aName)
+{
+  // We have to use this slightly awkward for loop since uint32_t >= 0 is an
+  // error for being always true.
+  for (uint32_t i = mFormData.Length(); i-- > 0; ) {
+    if (aName.Equals(mFormData[i].name)) {
+      mFormData.RemoveElementAt(i);
+    }
+  }
+}
+
+void
+nsFormData::ExtractValue(const FormDataTuple& aTuple,
+                         OwningFileOrUSVString* aOutValue)
+{
+  if (aTuple.valueIsFile) {
+    aOutValue->SetAsFile() = aTuple.fileValue;
+  } else {
+    aOutValue->SetAsUSVString() = aTuple.stringValue;
+  }
+}
+
+void
+nsFormData::Get(const nsAString& aName,
+                Nullable<OwningFileOrUSVString>& aOutValue)
+{
+  for (uint32_t i = 0; i < mFormData.Length(); ++i) {
+    if (aName.Equals(mFormData[i].name)) {
+      ExtractValue(mFormData[i], &aOutValue.SetValue());
+      return;
+    }
+  }
+
+  aOutValue.SetNull();
+}
+
+void
+nsFormData::GetAll(const nsAString& aName,
+                   nsTArray<OwningFileOrUSVString>& aValues)
+{
+  for (uint32_t i = 0; i < mFormData.Length(); ++i) {
+    if (aName.Equals(mFormData[i].name)) {
+      OwningFileOrUSVString* element = aValues.AppendElement();
+      ExtractValue(mFormData[i], element);
+    }
+  }
+}
+
+bool
+nsFormData::Has(const nsAString& aName)
+{
+  for (uint32_t i = 0; i < mFormData.Length(); ++i) {
+    if (aName.Equals(mFormData[i].name)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+nsFormData::FormDataTuple*
+nsFormData::RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName)
+{
+  FormDataTuple* lastFoundTuple = nullptr;
+  uint32_t lastFoundIndex = mFormData.Length();
+  // We have to use this slightly awkward for loop since uint32_t >= 0 is an
+  // error for being always true.
+  for (uint32_t i = mFormData.Length(); i-- > 0; ) {
+    if (aName.Equals(mFormData[i].name)) {
+      if (lastFoundTuple) {
+        // The one we found earlier was not the first one, we can remove it.
+        mFormData.RemoveElementAt(lastFoundIndex);
+      }
+
+      lastFoundTuple = &mFormData[i];
+      lastFoundIndex = i;
+    }
+  }
+
+  return lastFoundTuple;
+}
+
+void
+nsFormData::Set(const nsAString& aName, File& aBlob,
+                const Optional<nsAString>& aFilename)
+{
+  FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
+  if (tuple) {
+    nsAutoString filename;
+    if (aFilename.WasPassed()) {
+      filename = aFilename.Value();
+    } else {
+      filename.SetIsVoid(true);
+    }
+
+    SetNameFilePair(tuple, aName, &aBlob, filename);
+  } else {
+    Append(aName, aBlob, aFilename);
+  }
+}
+
+void
+nsFormData::Set(const nsAString& aName, const nsAString& aValue)
+{
+  FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
+  if (tuple) {
+    SetNameValuePair(tuple, aName, aValue);
+  } else {
+    Append(aName, aValue);
+  }
+}
+
 // -------------------------------------------------------------------------
 // nsIDOMFormData
 
 NS_IMETHODIMP
 nsFormData::Append(const nsAString& aName, nsIVariant* aValue)
 {
   uint16_t dataType;
   nsresult rv = aValue->GetDataType(&dataType);
--- a/dom/base/nsFormData.h
+++ b/dom/base/nsFormData.h
@@ -8,34 +8,75 @@
 #include "mozilla/Attributes.h"
 #include "nsIDOMFormData.h"
 #include "nsIXMLHttpRequest.h"
 #include "nsFormSubmission.h"
 #include "nsWrapperCache.h"
 #include "nsTArray.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/FormDataBinding.h"
 
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 class File;
 class HTMLFormElement;
 class GlobalObject;
 } // namespace dom
 } // namespace mozilla
 
 class nsFormData MOZ_FINAL : public nsIDOMFormData,
                              public nsIXHRSendable,
                              public nsFormSubmission,
                              public nsWrapperCache
 {
+private:
   ~nsFormData() {}
 
+  typedef mozilla::dom::File File;
+  struct FormDataTuple
+  {
+    nsString name;
+    nsString stringValue;
+    nsRefPtr<File> fileValue;
+    nsString filename;
+    bool valueIsFile;
+  };
+
+  // Returns the FormDataTuple to modify. This may be null, in which case
+  // no element with aName was found.
+  FormDataTuple*
+  RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName);
+
+  void SetNameValuePair(FormDataTuple* aData,
+                        const nsAString& aName,
+                        const nsAString& aValue)
+  {
+    MOZ_ASSERT(aData);
+    aData->name = aName;
+    aData->stringValue = aValue;
+    aData->valueIsFile = false;
+  }
+
+  void SetNameFilePair(FormDataTuple* aData,
+                       const nsAString& aName,
+                       File* aBlob,
+                       const nsAString& aFilename)
+  {
+    MOZ_ASSERT(aData);
+    aData->name = aName;
+    aData->fileValue = aBlob;
+    aData->filename = aFilename;
+    aData->valueIsFile = true;
+  }
+
+  void ExtractValue(const FormDataTuple& aTuple,
+                    mozilla::dom::OwningFileOrUSVString* aOutValue);
 public:
   explicit nsFormData(nsISupports* aOwner = nullptr);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFormData,
                                                          nsIDOMFormData)
 
   NS_DECL_NSIDOMFORMDATA
@@ -50,51 +91,43 @@ public:
   {
     return mOwner;
   }
   static already_AddRefed<nsFormData>
   Constructor(const mozilla::dom::GlobalObject& aGlobal,
               const mozilla::dom::Optional<mozilla::dom::NonNull<mozilla::dom::HTMLFormElement> >& aFormElement,
               mozilla::ErrorResult& aRv);
   void Append(const nsAString& aName, const nsAString& aValue);
-  void Append(const nsAString& aName, mozilla::dom::File& aBlob,
+  void Append(const nsAString& aName, File& aBlob,
               const mozilla::dom::Optional<nsAString>& aFilename);
+  void Delete(const nsAString& aName);
+  void Get(const nsAString& aName, mozilla::dom::Nullable<mozilla::dom::OwningFileOrUSVString>& aOutValue);
+  void GetAll(const nsAString& aName, nsTArray<mozilla::dom::OwningFileOrUSVString>& aValues);
+  bool Has(const nsAString& aName);
+  void Set(const nsAString& aName, File& aBlob,
+           const mozilla::dom::Optional<nsAString>& aFilename);
+  void Set(const nsAString& aName, const nsAString& aValue);
 
   // nsFormSubmission
   virtual nsresult GetEncodedSubmission(nsIURI* aURI,
                                         nsIInputStream** aPostDataStream) MOZ_OVERRIDE;
   virtual nsresult AddNameValuePair(const nsAString& aName,
                                     const nsAString& aValue) MOZ_OVERRIDE
   {
     FormDataTuple* data = mFormData.AppendElement();
-    data->name = aName;
-    data->stringValue = aValue;
-    data->valueIsFile = false;
+    SetNameValuePair(data, aName, aValue);
     return NS_OK;
   }
   virtual nsresult AddNameFilePair(const nsAString& aName,
-                                   nsIDOMBlob* aBlob,
+                                   File* aBlob,
                                    const nsString& aFilename) MOZ_OVERRIDE
   {
     FormDataTuple* data = mFormData.AppendElement();
-    data->name = aName;
-    data->fileValue = aBlob;
-    data->filename = aFilename;
-    data->valueIsFile = true;
+    SetNameFilePair(data, aName, aBlob, aFilename);
     return NS_OK;
   }
-
 private:
   nsCOMPtr<nsISupports> mOwner;
 
-  struct FormDataTuple
-  {
-    nsString name;
-    nsString stringValue;
-    nsCOMPtr<nsIDOMBlob> fileValue;
-    nsString filename;
-    bool valueIsFile;
-  };
-
   nsTArray<FormDataTuple> mFormData;
 };
 
 #endif // nsFormData_h__
--- a/dom/html/nsFormSubmission.cpp
+++ b/dom/html/nsFormSubmission.cpp
@@ -33,16 +33,17 @@
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIStringBundle.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIFileStreams.h"
 #include "nsContentUtils.h"
 
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/File.h"
 
 using namespace mozilla;
 using mozilla::dom::EncodingUtils;
 
 static void
 SendJSWarning(nsIDocument* aDocument,
               const char* aWarningName,
               const char16_t** aWarningArgs, uint32_t aWarningArgsLen)
@@ -73,17 +74,17 @@ public:
       mDocument(aDocument),
       mWarnedFileControl(false)
   {
   }
 
   virtual nsresult AddNameValuePair(const nsAString& aName,
                                     const nsAString& aValue);
   virtual nsresult AddNameFilePair(const nsAString& aName,
-                                   nsIDOMBlob* aBlob,
+                                   File* aBlob,
                                    const nsString& aFilename);
   virtual nsresult GetEncodedSubmission(nsIURI* aURI,
                                         nsIInputStream** aPostDataStream);
 
   virtual bool SupportsIsindexSubmission()
   {
     return true;
   }
@@ -160,28 +161,27 @@ nsFSURLEncoded::AddIsindex(const nsAStri
     mQueryString += NS_LITERAL_CSTRING("&isindex=") + convValue;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsFSURLEncoded::AddNameFilePair(const nsAString& aName,
-                                nsIDOMBlob* aBlob,
+                                File* aBlob,
                                 const nsString& aFilename)
 {
   if (!mWarnedFileControl) {
     SendJSWarning(mDocument, "ForgotFileEnctypeWarning", nullptr, 0);
     mWarnedFileControl = true;
   }
 
   nsAutoString filename;
-  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
-  if (file) {
-    file->GetName(filename);
+  if (aBlob && aBlob->IsFile()) {
+    aBlob->GetName(filename);
   }
 
   return AddNameValuePair(aName, filename);
 }
 
 static void
 HandleMailtoSubject(nsCString& aPath) {
 
@@ -436,17 +436,17 @@ nsFSMultipartFormData::AddNameValuePair(
                  + nameStr + NS_LITERAL_CSTRING("\"" CRLF CRLF)
                  + valueStr + NS_LITERAL_CSTRING(CRLF);
 
   return NS_OK;
 }
 
 nsresult
 nsFSMultipartFormData::AddNameFilePair(const nsAString& aName,
-                                       nsIDOMBlob* aBlob,
+                                       File* aBlob,
                                        const nsString& aFilename)
 {
   // Encode the control name
   nsAutoCString nameStr;
   nsresult rv = EncodeVal(aName, nameStr, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCString filename, contentType;
@@ -454,27 +454,26 @@ nsFSMultipartFormData::AddNameFilePair(c
   if (aBlob) {
     // We prefer the explicitly passed filename
     if (!aFilename.IsVoid()) {
       rv = EncodeVal(aFilename, filename, true);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       // Get and encode the filename
       nsAutoString filename16;
-      nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
-      if (file) {
-        rv = file->GetName(filename16);
+      if (aBlob->IsFile()) {
+        rv = aBlob->GetName(filename16);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       if (filename16.IsEmpty()) {
         filename16.AssignLiteral("blob");
       } else {
         nsAutoString filepath16;
-        rv = file->GetPath(filepath16);
+        rv = aBlob->GetPath(filepath16);
         NS_ENSURE_SUCCESS(rv, rv);
         if (!filepath16.IsEmpty()) {
           // File.path includes trailing "/"
           filename16 = filepath16 + filename16;
         }
       }
 
       rv = EncodeVal(filename16, filename, true);
@@ -593,17 +592,17 @@ public:
   nsFSTextPlain(const nsACString& aCharset, nsIContent* aOriginatingElement)
     : nsEncodingFormSubmission(aCharset, aOriginatingElement)
   {
   }
 
   virtual nsresult AddNameValuePair(const nsAString& aName,
                                     const nsAString& aValue);
   virtual nsresult AddNameFilePair(const nsAString& aName,
-                                   nsIDOMBlob* aBlob,
+                                   File* aBlob,
                                    const nsString& aFilename);
   virtual nsresult GetEncodedSubmission(nsIURI* aURI,
                                         nsIInputStream** aPostDataStream);
 
 private:
   nsString mBody;
 };
 
@@ -617,23 +616,22 @@ nsFSTextPlain::AddNameValuePair(const ns
   mBody.Append(aName + NS_LITERAL_STRING("=") + aValue +
                NS_LITERAL_STRING(CRLF));
 
   return NS_OK;
 }
 
 nsresult
 nsFSTextPlain::AddNameFilePair(const nsAString& aName,
-                               nsIDOMBlob* aBlob,
+                               File* aBlob,
                                const nsString& aFilename)
 {
   nsAutoString filename;
-  nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
-  if (file) {
-    file->GetName(filename);
+  if (aBlob && aBlob->IsFile()) {
+    aBlob->GetName(filename);
   }
 
   AddNameValuePair(aName, filename);
   return NS_OK;
 }
 
 nsresult
 nsFSTextPlain::GetEncodedSubmission(nsIURI* aURI,
--- a/dom/html/nsFormSubmission.h
+++ b/dom/html/nsFormSubmission.h
@@ -15,17 +15,22 @@ class nsGenericHTMLElement;
 class nsILinkHandler;
 class nsIContent;
 class nsIFormControl;
 class nsIDOMHTMLElement;
 class nsIDocShell;
 class nsIRequest;
 class nsISaveAsCharset;
 class nsIMultiplexInputStream;
-class nsIDOMBlob;
+
+namespace mozilla {
+namespace dom {
+class File;
+} // namespace dom
+} // namespace mozilla
 
 /**
  * Class for form submissions; encompasses the function to call to submit as
  * well as the form submission name/value pairs
  */
 class nsFormSubmission
 {
 public:
@@ -46,19 +51,19 @@ public:
   /**
    * Submit a name/file pair
    *
    * @param aName the name of the parameter
    * @param aBlob the file to submit
    * @param aFilename the filename to be used (not void)
    */
   virtual nsresult AddNameFilePair(const nsAString& aName,
-                                   nsIDOMBlob* aBlob,
+                                   mozilla::dom::File* aBlob,
                                    const nsString& aFilename) = 0;
-  
+
   /**
    * Reports whether the instance supports AddIsindex().
    *
    * @return true if supported.
    */
   virtual bool SupportsIsindexSubmission()
   {
     return false;
@@ -156,17 +161,17 @@ public:
    */
   nsFSMultipartFormData(const nsACString& aCharset,
                         nsIContent* aOriginatingElement);
   ~nsFSMultipartFormData();
  
   virtual nsresult AddNameValuePair(const nsAString& aName,
                                     const nsAString& aValue) MOZ_OVERRIDE;
   virtual nsresult AddNameFilePair(const nsAString& aName,
-                                   nsIDOMBlob* aBlob,
+                                   mozilla::dom::File* aBlob,
                                    const nsString& aFilename) MOZ_OVERRIDE;
   virtual nsresult GetEncodedSubmission(nsIURI* aURI,
                                         nsIInputStream** aPostDataStream) MOZ_OVERRIDE;
 
   void GetContentType(nsACString& aContentType)
   {
     aContentType =
       NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary;
--- a/dom/html/test/test_formData.html
+++ b/dom/html/test/test_formData.html
@@ -9,17 +9,100 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=690659">Mozilla Bug 690659</a>
 <script type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
-function runTest() {
+function testHas() {
+  var f = new FormData();
+  f.append("foo", "bar");
+  f.append("another", "value");
+  ok(f.has("foo"), "has() on existing name should be true.");
+  ok(f.has("another"), "has() on existing name should be true.");
+  ok(!f.has("nonexistent"), "has() on non-existent name should be false.");
+}
+
+function testGet() {
+  var f = new FormData();
+  f.append("foo", "bar");
+  f.append("foo", "bar2");
+  f.append("blob", new Blob(["hey"], { type: 'text/plain' }));
+  f.append("file", new File(["hey"], 'testname',  {type: 'text/plain'}));
+
+  is(f.get("foo"), "bar", "get() on existing name should return first value");
+  ok(f.get("blob") instanceof Blob, "get() on existing name should return first value");
+  is(f.get("blob").type, 'text/plain', "get() on existing name should return first value");
+  ok(f.get("file") instanceof File, "get() on existing name should return first value");
+  is(f.get("file").name, 'testname', "get() on existing name should return first value");
+
+  is(f.get("nonexistent"), null, "get() on non-existent name should return null.");
+}
+
+function testGetAll() {
+  var f = new FormData();
+  f.append("other", "value");
+  f.append("foo", "bar");
+  f.append("foo", "bar2");
+  f.append("foo", new Blob(["hey"], { type: 'text/plain' }));
+
+  var arr = f.getAll("foo");
+  is(arr.length, 3, "getAll() should retrieve all matching entries.");
+  is(arr[0], "bar", "values should match and be in order");
+  is(arr[1], "bar2", "values should match and be in order");
+  ok(arr[2] instanceof Blob, "values should match and be in order");
+
+  is(f.get("nonexistent"), null, "get() on non-existent name should return null.");
+}
+
+function testDelete() {
+  var f = new FormData();
+  f.append("other", "value");
+  f.append("foo", "bar");
+  f.append("foo", "bar2");
+  f.append("foo", new Blob(["hey"], { type: 'text/plain' }));
+
+  ok(f.has("foo"), "has() on existing name should be true.");
+  f.delete("foo");
+  ok(!f.has("foo"), "has() on deleted name should be false.");
+  is(f.getAll("foo").length, 0, "all entries should be deleted.");
+
+  is(f.getAll("other").length, 1, "other names should still be there.");
+  f.delete("other");
+  is(f.getAll("other").length, 0, "all entries should be deleted.");
+}
+
+function testSet() {
+  var f = new FormData();
+
+  f.set("other", "value");
+  ok(f.has("other"), "set() on new name should be similar to append()");
+  is(f.getAll("other").length, 1, "set() on new name should be similar to append()");
+
+  f.append("other", "value2");
+  is(f.getAll("other").length, 2, "append() should not replace existing entries.");
+
+  f.append("foo", "bar");
+  f.append("other", "value3");
+  f.append("other", "value3");
+  f.append("other", "value3");
+  is(f.getAll("other").length, 5, "append() should not replace existing entries.");
+
+  f.set("other", "value4");
+  is(f.getAll("other").length, 1, "set() should replace existing entries.");
+  is(f.getAll("other")[0], "value4", "set() should replace existing entries.");
+}
+
+function testIterate() {
+  todo(false, "Implement this in Bug 1085284.");
+}
+
+function testSend() {
     var xhr = new XMLHttpRequest();
     xhr.open("POST", "form_submit_server.sjs");
     xhr.onload = function () {
         var response = xhr.response;
 
         for (var entry of response) {
             is(entry.body, 'hey');
             is(entry.headers['Content-Type'], 'text/plain');
@@ -57,12 +140,23 @@ function runTest() {
     file = new File([blob], '',  {type: 'text/plain'});
     fd.append("empty-file-name", file);
     file = new File([blob], 'testname',  {type: 'text/plain'});
     fd.append("file-name-overwrite", file, "overwrite");
     xhr.responseType = 'json';
     xhr.send(fd);
 }
 
+function runTest() {
+  testHas();
+  testGet();
+  testGetAll();
+  testDelete();
+  testSet();
+  testIterate();
+  // Finally, send an XHR and verify the response matches.
+  testSend();
+}
+
 runTest()
 </script>
 </body>
 </html>
--- a/dom/webidl/FormData.webidl
+++ b/dom/webidl/FormData.webidl
@@ -2,13 +2,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * http://xhr.spec.whatwg.org
  */
 
+typedef (File or USVString) FormDataEntryValue;
+
 [Constructor(optional HTMLFormElement form)]
 interface FormData {
-  void append(DOMString name, Blob value, optional DOMString filename);
-  void append(DOMString name, DOMString value);
+  void append(USVString name, Blob value, optional USVString filename);
+  void append(USVString name, USVString value);
+  void delete(USVString name);
+  FormDataEntryValue? get(USVString name);
+  sequence<FormDataEntryValue> getAll(USVString name);
+  boolean has(USVString name);
+  void set(USVString name, Blob value, optional USVString filename);
+  void set(USVString name, USVString value);
+  // iterable<USVString, FormDataEntryValue>; - Bug 1127703
 };