Commit d8be2d18 authored by jianli@chromium.org's avatar jianli@chromium.org

Support using FormData to send a sliced file via XHR.

https://bugs.webkit.org/show_bug.cgi?id=36678

Reviewed by Dmitry Titov.

WebCore:

Tests: http/tests/local/formdata/send-form-data-with-sliced-file.html

* html/Blob.h:
(WebCore::Blob::isFile):
* html/DOMFormData.cpp:
(WebCore::DOMFormData::append):
* html/File.h:
(WebCore::File::isFile):
* html/FormDataList.h:
(WebCore::FormDataList::appendBlob):
(WebCore::FormDataList::Item::Item):
(WebCore::FormDataList::Item::blob):
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::appendFormData):
* platform/network/FormData.cpp:
(WebCore::FormData::appendDOMFormData):
* platform/network/mac/FormDataStreamMac.mm:
(WebCore::closeCurrentStream):

LayoutTests:

Move common functionality to test sending FormData into a helper file
so that it can be shared by FormData layout tests.
Also move the form data testing files to a new subdirectory formdata/.

* http/tests/local/formdata/resources/send-form-data-common.js: Added.
* http/tests/local/formdata/script-tests/TEMPLATE.html: Added.
* http/tests/local/formdata/script-tests/send-form-data-with-sliced-file.js: Added.
* http/tests/local/formdata/script-tests/send-form-data.js: Added.
* http/tests/local/formdata/send-form-data-expected.txt: Renamed from LayoutTests/http/tests/local/send-form-data-expected.txt.
* http/tests/local/formdata/send-form-data-with-sliced-file-expected.txt: Added.
* http/tests/local/formdata/send-form-data-with-sliced-file.html: Added.
* http/tests/local/formdata/send-form-data.html: Added.
* http/tests/local/resources/send-form-data.js: Removed.
* http/tests/local/send-form-data.html: Removed.
* platform/gtk/Skipped:
* platform/qt/Skipped:
* platform/win/Skipped:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@57695 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent d8853559
2010-04-15 Jian Li <jianli@chromium.org>
Reviewed by Dmitry Titov.
Support using FormData to send a sliced file via XHR.
https://bugs.webkit.org/show_bug.cgi?id=36678
Move common functionality to test sending FormData into a helper file
so that it can be shared by FormData layout tests.
Also move the form data testing files to a new subdirectory formdata/.
* http/tests/local/formdata/resources/send-form-data-common.js: Added.
* http/tests/local/formdata/script-tests/TEMPLATE.html: Added.
* http/tests/local/formdata/script-tests/send-form-data-with-sliced-file.js: Added.
* http/tests/local/formdata/script-tests/send-form-data.js: Added.
* http/tests/local/formdata/send-form-data-expected.txt: Renamed from LayoutTests/http/tests/local/send-form-data-expected.txt.
* http/tests/local/formdata/send-form-data-with-sliced-file-expected.txt: Added.
* http/tests/local/formdata/send-form-data-with-sliced-file.html: Added.
* http/tests/local/formdata/send-form-data.html: Added.
* http/tests/local/resources/send-form-data.js: Removed.
* http/tests/local/send-form-data.html: Removed.
* platform/gtk/Skipped:
* platform/qt/Skipped:
* platform/win/Skipped:
2010-04-15 Zhenyao Mo <zmo@google.com>
Reviewed by Dimitri Glazkov.
......
description("Test for sending FormData via XMLHttpRequest.");
var fileInput = document.createElement("input");
fileInput.type = 'file';
fileInput.style.width = "100%"; // So that any manual testing will show full file names
......@@ -21,7 +19,7 @@ function getFileName(filePath)
return filePath.substr(index + 1);
}
function sendFormData(formDataList)
function sendFormData(formDataList, fileSliced)
{
var formData = new FormData();
for (var i = 0; i < formDataList.length; i++)
......@@ -30,8 +28,14 @@ function sendFormData(formDataList)
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:8000/xmlhttprequest/resources/multipart-post-echo.php", false);
xhr.send(formData);
var responseText = xhr.responseText;
// If we're sending a sliced file among the FormData, a uniquely generated name prefixed with "Blob" is used.
// We need to remove the random part in the filename so that we get consistent result.
if (fileSliced)
responseText = responseText.replace(/Blob\w{32}/g, "Blob");
debug(xhr.responseText);
debug(responseText);
}
function testSendingFormData(dataList)
......@@ -50,12 +54,18 @@ function testSendingFormData(dataList)
var files = fileInput.files;
var formDataList = [];
var fileSliced = false;
for (var i = 0; i < dataList.length; i++) {
if (dataList[i]['type'] == 'file') {
var fileName = getFileName(dataList[i]['value']);
for (var j = 0; j < files.length; j++) {
if (fileName == files[j].name) {
formDataList.push({'name': dataList[i]['name'], 'value': files[j]});
var file = files[j];
if (dataList[i]['start'] && dataList[i]['length']) {
fileSliced = true;
file = file.slice(dataList[i]['start'], dataList[i]['length']);
}
formDataList.push({'name': dataList[i]['name'], 'value': file});
break;
}
}
......@@ -64,52 +74,5 @@ function testSendingFormData(dataList)
}
}
sendFormData(formDataList);
sendFormData(formDataList, fileSliced);
}
function runTest()
{
debug("Sending FormData containing one string with empty name:");
testSendingFormData([
{ 'type': 'string', 'name': '', 'value': 'foo' }
]);
debug("Sending FormData containing one file with empty name:");
testSendingFormData([
{ 'type': 'file', 'name': '', 'value': 'resources/file-for-drag-to-send.txt' }
]);
debug("Sending FormData containing one string:");
testSendingFormData([
{ 'type': 'string', 'name': 'string', 'value': 'foo' }
]);
debug("Sending FormData containing one file:");
testSendingFormData([
{ 'type': 'file', 'name': 'file', 'value': 'resources/file-for-drag-to-send.txt' }
]);
debug("Sending FormData containing one string and one file:");
testSendingFormData([
{ 'type': 'string', 'name': 'string1', 'value': 'foo' },
{ 'type': 'file', 'name': 'file1', 'value': 'resources/file-for-drag-to-send.txt' }
]);
debug("Sending FormData containing two strings and two files:");
testSendingFormData([
{ 'type': 'string', 'name': 'string1', 'value': 'foo' },
{ 'type': 'file', 'name': 'file1', 'value': 'resources/file-for-drag-to-send.txt' },
{ 'type': 'string', 'name': 'string2', 'value': 'bar' },
{ 'type': 'file', 'name': 'file2', 'value': 'resources/file-for-drag-to-send.txt' }
]);
}
if (window.eventSender) {
runTest();
// Clean up after ourselves
fileInput.parentNode.removeChild(fileInput);
} else {
testFailed("This test is not interactive, please run using DumpRenderTree");
}
var successfullyParsed = true;
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" href="../../../fast/js/resources/js-test-style.css">
<script src="../../../fast/js/resources/js-test-pre.js"></script>
<link rel="stylesheet" href="../../../../fast/js/resources/js-test-style.css">
<script src="../../../../fast/js/resources/js-test-pre.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script src="resources/send-form-data.js"></script>
<script src="../../../fast/js/resources/js-test-post.js"></script>
<script src="resources/send-form-data-common.js"></script>
<script src="YOUR_JS_FILE_HERE"></script>
<script src="../../../../fast/js/resources/js-test-post.js"></script>
</body>
</html>
description("Test for sending FormData with sliced files via XMLHttpRequest.");
function runTest()
{
debug("Sending FormData containing one sliced file with empty name:");
testSendingFormData([
{ 'type': 'file', 'name': '', 'value': '../resources/file-for-drag-to-send.txt' }
]);
debug("Sending FormData containing one sliced file:");
testSendingFormData([
{ 'type': 'file', 'name': 'file', 'value': '../resources/file-for-drag-to-send.txt', 'start' : 1, 'length' : 5 }
]);
debug("Sending FormData containing one string and one sliced file:");
testSendingFormData([
{ 'type': 'string', 'name': 'string1', 'value': 'foo' },
{ 'type': 'file', 'name': 'file1', 'value': '../resources/file-for-drag-to-send.txt', 'start' : 1, 'length' : 5 }
]);
debug("Sending FormData containing two strings and two sliced files:");
testSendingFormData([
{ 'type': 'string', 'name': 'string1', 'value': 'foo' },
{ 'type': 'file', 'name': 'file1', 'value': '../resources/file-for-drag-to-send.txt', 'start' : 1, 'length' : 5 },
{ 'type': 'string', 'name': 'string2', 'value': 'bar' },
{ 'type': 'file', 'name': 'file2', 'value': '../resources/file-for-drag-to-send.txt', 'start' : 3, 'length' : 2 }
]);
}
if (window.eventSender) {
runTest();
// Clean up after ourselves
fileInput.parentNode.removeChild(fileInput);
} else {
testFailed("This test is not interactive, please run using DumpRenderTree");
}
var successfullyParsed = true;
description("Test for sending FormData via XMLHttpRequest.");
function runTest()
{
debug("Sending FormData containing one string with empty name:");
testSendingFormData([
{ 'type': 'string', 'name': '', 'value': 'foo' }
]);
debug("Sending FormData containing one file with empty name:");
testSendingFormData([
{ 'type': 'file', 'name': '', 'value': '../resources/file-for-drag-to-send.txt' }
]);
debug("Sending FormData containing one string:");
testSendingFormData([
{ 'type': 'string', 'name': 'string', 'value': 'foo' }
]);
debug("Sending FormData containing one file:");
testSendingFormData([
{ 'type': 'file', 'name': 'file', 'value': '../resources/file-for-drag-to-send.txt' }
]);
debug("Sending FormData containing one string and one file:");
testSendingFormData([
{ 'type': 'string', 'name': 'string1', 'value': 'foo' },
{ 'type': 'file', 'name': 'file1', 'value': '../resources/file-for-drag-to-send.txt' }
]);
debug("Sending FormData containing two strings and two files:");
testSendingFormData([
{ 'type': 'string', 'name': 'string1', 'value': 'foo' },
{ 'type': 'file', 'name': 'file1', 'value': '../resources/file-for-drag-to-send.txt' },
{ 'type': 'string', 'name': 'string2', 'value': 'bar' },
{ 'type': 'file', 'name': 'file2', 'value': '../resources/file-for-drag-to-send.txt' }
]);
}
if (window.eventSender) {
runTest();
// Clean up after ourselves
fileInput.parentNode.removeChild(fileInput);
} else {
testFailed("This test is not interactive, please run using DumpRenderTree");
}
var successfullyParsed = true;
Test for sending FormData with sliced files via XMLHttpRequest.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Sending FormData containing one sliced file with empty name:
Sending FormData containing one sliced file:
file=Blob:23456
Sending FormData containing one string and one sliced file:
string1=foo&file1=Blob:23456
Sending FormData containing two strings and two sliced files:
string1=foo&string2=bar&file1=Blob:23456&file2=Blob:45
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" href="../../../../fast/js/resources/js-test-style.css">
<script src="../../../../fast/js/resources/js-test-pre.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script src="resources/send-form-data-common.js"></script>
<script src="script-tests/send-form-data-with-sliced-file.js"></script>
<script src="../../../../fast/js/resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<link rel="stylesheet" href="../../../../fast/js/resources/js-test-style.css">
<script src="../../../../fast/js/resources/js-test-pre.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script src="resources/send-form-data-common.js"></script>
<script src="script-tests/send-form-data.js"></script>
<script src="../../../../fast/js/resources/js-test-post.js"></script>
</body>
</html>
......@@ -3427,6 +3427,7 @@ http/tests/loading/text-content-type-with-binary-extension.html
http/tests/local/send-dragged-file.html
http/tests/local/send-sliced-dragged-file.html
http/tests/local/send-form-data.html
http/tests/local/send-form-data-with-sliced-file.html
http/tests/media/video-play-stall-seek.html
http/tests/media/video-play-stall.html
http/tests/media/video-seekable-stall.html
......
......@@ -50,6 +50,7 @@ http/tests/local/drag-over-remote-content.html
http/tests/local/send-dragged-file.html
http/tests/local/send-sliced-dragged-file.html
http/tests/local/send-form-data.html
http/tests/local/send-form-data-with-sliced-file.html
# Failing navigation tests
http/tests/navigation/anchor-frames.html
......
......@@ -670,6 +670,7 @@ fast/forms/input-selectedoption.html
http/tests/local/send-dragged-file.html
http/tests/local/send-sliced-dragged-file.html
http/tests/local/send-form-data.html
http/tests/local/send-form-data-with-sliced-file.html
# <https://bugs.webkit.org/show_bug.cgi?id=29289>
plugins/destroy-during-npp-new.html
......
2010-04-15 Jian Li <jianli@chromium.org>
Reviewed by Dmitry Titov.
Support using FormData to send a sliced file via XHR.
https://bugs.webkit.org/show_bug.cgi?id=36678
Tests: http/tests/local/formdata/send-form-data-with-sliced-file.html
* html/Blob.h:
(WebCore::Blob::isFile):
* html/DOMFormData.cpp:
(WebCore::DOMFormData::append):
* html/File.h:
(WebCore::File::isFile):
* html/FormDataList.h:
(WebCore::FormDataList::appendBlob):
(WebCore::FormDataList::Item::Item):
(WebCore::FormDataList::Item::blob):
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::appendFormData):
* platform/network/FormData.cpp:
(WebCore::FormData::appendDOMFormData):
* platform/network/mac/FormDataStreamMac.mm:
(WebCore::closeCurrentStream):
2010-04-15 Zhenyao Mo <zmo@google.com>
Reviewed by Dimitri Glazkov.
......@@ -53,6 +53,8 @@ public:
virtual ~Blob() { }
virtual bool isFile() const { return false; }
#if ENABLE(BLOB_SLICE)
PassRefPtr<Blob> slice(long long start, long long length) const;
#endif
......
......@@ -50,11 +50,8 @@ void DOMFormData::append(const String& name, const String& value)
void DOMFormData::append(const String& name, Blob* blob)
{
// FIXME: Need to support sliced file when Blob.slice support is landed.
if (!name.isEmpty() && !blob->path().isEmpty()) {
RefPtr<File> file = static_cast<File*>(blob);
appendFile(name, file.release());
}
if (!name.isEmpty())
appendBlob(name, blob);
}
} // namespace WebCore
......@@ -39,6 +39,8 @@ public:
return adoptRef(new File(path));
}
virtual bool isFile() const { return true; }
const String& name() const { return m_name; }
const String& type() const { return m_type; }
......
......@@ -21,7 +21,7 @@
#ifndef FormDataList_h
#define FormDataList_h
#include "File.h"
#include "Blob.h"
#include "TextEncoding.h"
#include <wtf/text/CString.h>
......@@ -37,21 +37,21 @@ public:
{ appendString(key); appendString(value); }
void appendData(const String& key, int value)
{ appendString(key); appendString(String::number(value)); }
void appendFile(const String& key, PassRefPtr<File> file)
{ appendString(key); m_list.append(file); }
void appendBlob(const String& key, PassRefPtr<Blob> blob)
{ appendString(key); m_list.append(blob); }
class Item {
public:
Item() { }
Item(const WTF::CString& data) : m_data(data) { }
Item(PassRefPtr<File> file) : m_file(file) { }
Item(PassRefPtr<Blob> blob) : m_blob(blob) { }
const WTF::CString& data() const { return m_data; }
File* file() const { return m_file.get(); }
Blob* blob() const { return m_blob.get(); }
private:
WTF::CString m_data;
RefPtr<File> m_file;
RefPtr<Blob> m_blob;
};
const Vector<Item>& list() const { return m_list; }
......
......@@ -1354,12 +1354,12 @@ bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
// If no filename at all is entered, return successful but empty.
// Null would be more logical, but Netscape posts an empty file. Argh.
if (!numFiles) {
encoding.appendFile(name(), File::create(""));
encoding.appendBlob(name(), File::create(""));
return true;
}
for (unsigned i = 0; i < numFiles; ++i)
encoding.appendFile(name(), m_fileList->item(i));
encoding.appendBlob(name(), m_fileList->item(i));
return true;
}
}
......
......@@ -26,11 +26,13 @@
#include "ChromeClient.h"
#include "DOMFormData.h"
#include "Document.h"
#include "File.h"
#include "FileSystem.h"
#include "FormDataBuilder.h"
#include "MIMETypeRegistry.h"
#include "Page.h"
#include "TextEncoding.h"
#include "UUID.h"
namespace WebCore {
......@@ -186,9 +188,21 @@ void FormData::appendDOMFormData(const DOMFormData& domFormData, bool isMultiPar
bool shouldGenerateFile = false;
// If the current type is FILE, then we also need to include the filename
if (value.file()) {
const String& path = value.file()->path();
String fileName = value.file()->fileName();
if (value.blob()) {
const String& path = value.blob()->path();
#if ENABLE(BLOB_SLICE)
String fileName;
if (value.blob()->isFile())
fileName = static_cast<File*>(value.blob())->fileName();
else {
// If a blob is sliced from a file, it does not have the filename. In this case, let's produce a unique filename.
fileName = "Blob" + createCanonicalUUIDString();
fileName.replace("-", ""); // For safty, remove '-' from the filename snce some servers may not like it.
}
#else
ASSERT(value.blob()->isFile());
String fileName = static_cast<File*>(value.blob())->fileName();
#endif
// Let the application specify a filename if it's going to generate a replacement file for the upload.
if (!path.isEmpty()) {
......@@ -203,7 +217,12 @@ void FormData::appendDOMFormData(const DOMFormData& domFormData, bool isMultiPar
// We have to include the filename=".." part in the header, even if the filename is empty
formDataBuilder.addFilenameToMultiPartHeader(header, encoding, fileName);
// If a blob is sliced from a file, do not add the content type.
#if ENABLE(BLOB_SLICE)
if (!fileName.isEmpty() && value.blob()->isFile()) {
#else
if (!fileName.isEmpty()) {
#endif
// FIXME: The MIMETypeRegistry function's name makes it sound like it takes a path,
// not just a basename. But filename is not the path. But note that it's not safe to
// just use path instead since in the generated-file case it will not reflect the
......@@ -220,8 +239,12 @@ void FormData::appendDOMFormData(const DOMFormData& domFormData, bool isMultiPar
appendData(header.data(), header.size());
if (size_t dataSize = value.data().length())
appendData(value.data().data(), dataSize);
else if (value.file() && !value.file()->path().isEmpty())
appendFile(value.file()->path(), shouldGenerateFile);
else if (value.blob() && !value.blob()->path().isEmpty())
#if ENABLE(BLOB_SLICE)
appendFileRange(value.blob()->path(), value.blob()->start(), value.blob()->length(), value.blob()->modificationTime(), shouldGenerateFile);
#else
appendFile(value.blob()->path(), shouldGenerateFile);
#endif
appendData("\r\n", 2);
} else {
......
......@@ -141,6 +141,9 @@ static void closeCurrentStream(FormStreamFields *form)
CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
CFRelease(form->currentStream);
form->currentStream = NULL;
#if ENABLE(BLOB_SLICE)
form->currentStreamRangeLength = Blob::toEndOfFile;
#endif
}
if (form->currentData) {
fastFree(form->currentData);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment