Index: third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp |
diff --git a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp |
index 1c2e04da558ed62ec689bb10c226fd58dd62e337..f4c7c79f4a7e411bdd14a0cb34c65ba4836570b3 100644 |
--- a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp |
+++ b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp |
@@ -4,8 +4,13 @@ |
#include "modules/fetch/FetchDataLoader.h" |
+#include "core/fileapi/Blob.h" |
+#include "core/html/FormData.h" |
#include "core/html/parser/TextResourceDecoder.h" |
#include "modules/fetch/BytesConsumer.h" |
+#include "modules/fetch/MultipartParser.h" |
+#include "platform/HTTPNames.h" |
+#include "platform/network/ParsedContentType.h" |
#include "wtf/PtrUtil.h" |
#include "wtf/text/StringBuilder.h" |
#include "wtf/text/WTFString.h" |
@@ -168,6 +173,178 @@ private: |
std::unique_ptr<ArrayBufferBuilder> m_rawData; |
}; |
+class FetchDataLoaderAsFormData final : public FetchDataLoader, public BytesConsumer::Client, public MultipartParser::Client { |
yhirano
2016/09/21 09:02:57
Please add unittests.
e_hakkinen
2016/09/22 22:27:15
Done.
|
+ USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsFormData); |
+ |
+public: |
+ explicit FetchDataLoaderAsFormData(const String& multipartBoundary) |
+ : m_multipartBoundary(multipartBoundary) |
+ { |
+ } |
+ |
+ void start(BytesConsumer* consumer, FetchDataLoader::Client* client) override |
+ { |
+ DCHECK(!m_client); |
+ DCHECK(!m_multipartParser); |
+ DCHECK(!m_consumer); |
+ |
+ CString multipartBoundaryCString = m_multipartBoundary.utf8(); |
+ Vector<char> multipartBoundaryVector; |
+ multipartBoundaryVector.append(multipartBoundaryCString.data(), multipartBoundaryCString.length()); |
+ |
+ m_client = client; |
+ m_formData = FormData::create(); |
+ m_multipartParser = new MultipartParser(std::move(multipartBoundaryVector), this); |
+ m_consumer = consumer; |
+ m_consumer->setClient(this); |
+ onStateChange(); |
+ } |
+ |
+ void onStateChange() override |
+ { |
+ while (!m_multipartParser->isCancelled()) { |
+ const char* buffer; |
+ size_t available; |
+ switch (m_consumer->beginRead(&buffer, &available)) { |
+ case BytesConsumer::Result::Ok: |
+ if (available > 0) { |
+ if (!m_multipartParser->appendData(buffer, available)) |
+ failed(); |
yhirano
2016/09/21 09:02:57
BytesConsumer::cancel cannot be called in a two-ph
e_hakkinen
2016/09/22 22:27:15
Done.
|
+ } |
+ if (m_consumer->endRead(available) != BytesConsumer::Result::Ok) { |
yhirano
2016/09/21 09:02:57
I recently changed BytesConsumer::endRead so that
e_hakkinen
2016/09/22 22:27:16
Done.
|
+ m_client->didFetchDataLoadFailed(); |
yhirano
2016/09/21 09:02:57
failed()?
e_hakkinen
2016/09/22 22:27:16
Yeah, but there is not really need for failed() to
|
+ return; |
+ } |
+ break; |
+ case BytesConsumer::Result::ShouldWait: |
+ return; |
+ case BytesConsumer::Result::Done: |
+ if (m_multipartParser->finish()) { |
+ m_client->didFetchDataLoadedFormData(m_formData); |
+ } else { |
+ m_client->didFetchDataLoadFailed(); |
+ } |
+ return; |
+ case BytesConsumer::Result::Error: |
+ m_client->didFetchDataLoadFailed(); |
+ return; |
+ } |
+ } |
+ } |
+ |
+ void cancel() override |
+ { |
+ m_consumer->cancel(); |
+ m_multipartParser->cancel(); |
+ } |
+ |
+ DEFINE_INLINE_TRACE() |
+ { |
+ visitor->trace(m_consumer); |
+ visitor->trace(m_client); |
+ visitor->trace(m_formData); |
+ visitor->trace(m_multipartParser); |
+ FetchDataLoader::trace(visitor); |
+ BytesConsumer::Client::trace(visitor); |
+ MultipartParser::Client::trace(visitor); |
+ } |
+ |
+private: |
+ void failed() |
+ { |
+ m_client->didFetchDataLoadFailed(); |
+ // The client does not like to be notified multiple times thus stop |
+ // parsing so that no more errors will be reached. |
+ cancel(); |
+ } |
+ |
+ void partHeaderFieldsInMultipartReceived(const ResourceResponse& response) override |
+ { |
+ if (!m_currentEntry.initialize(response)) |
+ failed(); |
+ } |
+ |
+ void partDataInMultipartReceived(const char* bytes, size_t size) override |
+ { |
+ if (!m_currentEntry.appendBytes(bytes, size)) |
+ failed(); |
+ } |
+ |
+ void partDataInMultipartFullyReceived() override |
+ { |
+ if (!m_currentEntry.finish(m_formData)) |
+ failed(); |
+ } |
+ |
+ class Entry { |
+ public: |
+ bool initialize(const ResourceResponse& response) |
+ { |
+ ParsedContentType disposition(response.httpHeaderField(HTTPNames::Content_Disposition)); |
+ String dispositionType = disposition.mimeType(); |
+ m_filename = disposition.parameterValueForName("filename"); |
+ m_name = disposition.parameterValueForName("name"); |
+ m_blobData.reset(); |
+ m_stringBuilder.reset(); |
+ if (dispositionType != "form-data" || m_name.isNull()) |
+ return false; |
+ if (!m_filename.isNull()) { |
+ m_blobData = BlobData::create(); |
+ m_blobData->setContentType(response.httpHeaderField(HTTPNames::Content_Type)); |
+ } else { |
+ if (!m_decoder) |
+ m_decoder = TextResourceDecoder::createAlwaysUseUTF8ForText(); |
+ m_stringBuilder.reset(new StringBuilder); |
+ } |
+ return true; |
+ } |
+ |
+ bool appendBytes(const char* bytes, size_t size) |
+ { |
+ if (m_blobData) |
+ m_blobData->appendBytes(bytes, size); |
+ if (m_stringBuilder) { |
+ m_stringBuilder->append(m_decoder->decode(bytes, size)); |
+ if (m_decoder->sawError()) |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ bool finish(FormData* formData) |
+ { |
+ if (m_blobData) { |
+ DCHECK(!m_stringBuilder); |
+ auto size = m_blobData->length(); |
+ formData->append(m_name, Blob::create(BlobDataHandle::create(std::move(m_blobData), size)), m_filename); |
+ } |
+ if (m_stringBuilder) { |
+ DCHECK(!m_blobData); |
+ m_stringBuilder->append(m_decoder->flush()); |
+ if (m_decoder->sawError()) |
+ return false; |
+ formData->append(m_name, m_stringBuilder->toString()); |
+ } |
+ return true; |
+ } |
+ |
+ private: |
+ std::unique_ptr<BlobData> m_blobData; |
+ std::unique_ptr<TextResourceDecoder> m_decoder; |
+ String m_filename; |
+ String m_name; |
+ std::unique_ptr<StringBuilder> m_stringBuilder; |
+ }; |
+ |
+ Member<BytesConsumer> m_consumer; |
+ Member<FetchDataLoader::Client> m_client; |
+ Member<FormData> m_formData; |
+ Member<MultipartParser> m_multipartParser; |
+ |
+ Entry m_currentEntry; |
+ String m_multipartBoundary; |
+}; |
+ |
class FetchDataLoaderAsString final : public FetchDataLoader, public BytesConsumer::Client { |
USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsString); |
public: |
@@ -319,6 +496,11 @@ FetchDataLoader* FetchDataLoader::createLoaderAsArrayBuffer() |
return new FetchDataLoaderAsArrayBuffer(); |
} |
+FetchDataLoader* FetchDataLoader::createLoaderAsFormData(const String& multipartBoundary) |
+{ |
+ return new FetchDataLoaderAsFormData(multipartBoundary); |
+} |
+ |
FetchDataLoader* FetchDataLoader::createLoaderAsString() |
{ |
return new FetchDataLoaderAsString(); |