Bug 1255735 - "Firefox 45 does not send content-type in empty input[type=file] anymore". r=smaug, a=sylvestre
MozReview-Commit-ID: KR6QAauKLdp
--- a/dom/base/nsFormData.cpp
+++ b/dom/base/nsFormData.cpp
@@ -272,16 +272,17 @@ nsFormData::SetNameFilePair(FormDataTupl
File* aFile)
{
MOZ_ASSERT(aData);
aData->name = aName;
if (aFile) {
aData->value.SetAsFile() = aFile;
} else {
aData->value.SetAsUSVString() = EmptyString();
+ aData->wasNullBlob = true;
}
}
// -------------------------------------------------------------------------
// nsIDOMFormData
NS_IMETHODIMP
nsFormData::Append(const nsAString& aName, nsIVariant* aValue)
@@ -353,17 +354,20 @@ nsFormData::Constructor(const GlobalObje
NS_IMETHODIMP
nsFormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
nsACString& aContentType, nsACString& aCharset)
{
nsFSMultipartFormData fs(NS_LITERAL_CSTRING("UTF-8"), nullptr);
for (uint32_t i = 0; i < mFormData.Length(); ++i) {
- if (mFormData[i].value.IsFile()) {
+ if (mFormData[i].wasNullBlob) {
+ MOZ_ASSERT(mFormData[i].value.IsUSVString());
+ fs.AddNameFilePair(mFormData[i].name, nullptr);
+ } else if (mFormData[i].value.IsFile()) {
fs.AddNameFilePair(mFormData[i].name, mFormData[i].value.GetAsFile());
} else if (mFormData[i].value.IsUSVString()) {
fs.AddNameValuePair(mFormData[i].name,
mFormData[i].value.GetAsUSVString());
} else {
fs.AddNameFilePair(mFormData[i].name, nullptr);
}
}
--- a/dom/base/nsFormData.h
+++ b/dom/base/nsFormData.h
@@ -36,18 +36,21 @@ private:
~nsFormData() {}
typedef mozilla::dom::Blob Blob;
typedef mozilla::dom::File File;
typedef mozilla::dom::OwningFileOrUSVString OwningFileOrUSVString;
struct FormDataTuple
{
+ FormDataTuple() : wasNullBlob(false) {}
+
nsString name;
OwningFileOrUSVString value;
+ bool wasNullBlob;
};
// 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,
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1250148.sjs
@@ -0,0 +1,60 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+function utf8decode(s) {
+ return decodeURIComponent(escape(s));
+}
+
+function utf8encode(s) {
+ return unescape(encodeURIComponent(s));
+}
+
+function handleRequest(request, response) {
+ var bodyStream = new BinaryInputStream(request.bodyInputStream);
+
+ var requestBody = "";
+ while ((bodyAvail = bodyStream.available()) > 0) {
+ requestBody += bodyStream.readBytes(bodyAvail);
+ }
+
+ var result = [];
+
+ if (request.method == "POST") {
+ var contentTypeParams = {};
+ request.getHeader("Content-Type").split(/\s*\;\s*/).forEach(function(s) {
+ if (s.indexOf('=') >= 0) {
+ let [name, value] = s.split('=');
+ contentTypeParams[name] = value;
+ }
+ else {
+ contentTypeParams[''] = s;
+ }
+ });
+
+ if (contentTypeParams[''] == "multipart/form-data" &&
+ request.queryString == "") {
+ requestBody.split("--" + contentTypeParams.boundary).slice(1, -1).forEach(function (s) {
+
+ let headers = {};
+ let headerEnd = s.indexOf("\r\n\r\n");
+ s.substr(2, headerEnd-2).split("\r\n").forEach(function(s) {
+ // We're assuming UTF8 for now
+ let [name, value] = s.split(': ');
+ headers[name] = utf8decode(value);
+ });
+
+ let body = s.substring(headerEnd + 4, s.length - 2);
+ if (!headers["Content-Type"] || headers["Content-Type"] == "text/plain") {
+ // We're assuming UTF8 for now
+ body = utf8decode(body);
+ }
+ result.push({ headers: headers, body: body});
+ });
+ }
+ }
+
+ response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
+ response.write(utf8encode(JSON.stringify(result)));
+}
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -252,16 +252,17 @@ support-files =
iframe_postMessages.html
test_performance_observer.js
performance_observer.html
test_anonymousContent_style_csp.html^headers^
file_explicit_user_agent.sjs
referrer_change_server.sjs
file_change_policy_redirect.html
file_bug1198095.js
+ file_bug1250148.sjs
[test_anonymousContent_api.html]
[test_anonymousContent_append_after_reflow.html]
[test_anonymousContent_canvas.html]
skip-if = buildapp == 'b2g' # Requires webgl support
[test_anonymousContent_insert.html]
[test_anonymousContent_manipulate_content.html]
[test_anonymousContent_style_csp.html]
@@ -856,8 +857,9 @@ support-files = worker_postMessages.js
[test_frameLoader_switchProcess.html]
skip-if = e10s || os != 'linux' || buildapp != 'browser'
[test_explicit_user_agent.html]
[test_change_policy.html]
skip-if = buildapp == 'b2g' #no ssl support
[test_document.all_iteration.html]
[test_bug1198095.html]
[test_bug1187157.html]
+[test_bug1250148.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1250148.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1250148
+ -->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1250148 - FormData and HTML submission compatibility</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <form id="form" enctype="multipart/form-data"><input type="file" name="test" /></form>
+ <script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var f = document.getElementById('form');
+var fd = new FormData(f);
+is(fd.get("test"), "", "We want an empty string.");
+
+var getAll = fd.getAll("test");
+ok(Array.isArray(getAll), "We want an array with 1 empty string.");
+is(getAll.length, 1, "We want an array with 1 empty string.");
+is(getAll[0], "", "We want an array with 1 empty string.");
+
+var xhr = new XMLHttpRequest();
+xhr.open("POST", "file_bug1250148.sjs", true);
+xhr.onload = function() {
+ var obj = JSON.parse(xhr.responseText);
+
+ ok(Array.isArray(obj), "XHR response is an array.");
+ is(obj.length, 1, "XHR response array contains 1 result.");
+
+ ok("headers" in obj[0], "XHR response has an header value");
+
+ ok("Content-Disposition" in obj[0].headers, "XHR response.headers array has a Content-Desposition value");
+ is(obj[0].headers["Content-Disposition"], "form-data; name=\"test\"; filename=\"\"", "Correct Content-Disposition");
+
+ ok("Content-Type" in obj[0].headers, "XHR response.headers array has a Content-Type value");
+ is(obj[0].headers["Content-Type"], "application/octet-stream", "Correct Content-Type");
+
+ ok("body" in obj[0], "XHR response has a body value");
+ is(obj[0].body, "", "Correct body value");
+
+ SimpleTest.finish();
+}
+xhr.send(fd);
+
+ </script>
+ </body>
+</html>