Chromium Code Reviews| 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..566724aa35787f35072ce509316df8d7aa2b1437 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,176 @@ private: |
| std::unique_ptr<ArrayBufferBuilder> m_rawData; |
| }; |
| +class FetchDataLoaderAsFormData final : public FetchDataLoader, public BytesConsumer::Client, public MultipartParser::Client { |
| + 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(); |
|
horo
2016/09/27 10:45:49
nit: const
e_hakkinen
2016/09/28 15:15:04
Done.
|
| + 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 (true) { |
| + const char* buffer; |
| + size_t available; |
| + auto result = m_consumer->beginRead(&buffer, &available); |
| + if (result == BytesConsumer::Result::ShouldWait) |
| + return; |
| + if (result == BytesConsumer::Result::Ok) { |
| + bool bufferAppended = m_multipartParser->appendData(buffer, available); |
|
horo
2016/09/27 10:45:49
nit: const
e_hakkinen
2016/09/28 15:15:04
Done.
|
| + bool multipartReceiveFailed = m_multipartParser->isCancelled(); |
|
horo
2016/09/27 10:45:49
nit: const
e_hakkinen
2016/09/28 15:15:04
Done.
|
| + result = m_consumer->endRead(available); |
| + if (!bufferAppended || multipartReceiveFailed) |
| + result = BytesConsumer::Result::Error; |
| + } |
| + if (result == BytesConsumer::Result::Done) { |
| + if (m_multipartParser->finish()) { |
|
yhirano
2016/09/26 10:59:34
Why don't you move this section into the below swi
e_hakkinen
2016/09/28 15:15:04
Maybe done. I am not if I understood correctly wha
|
| + DCHECK(!m_multipartParser->isCancelled()); |
| + m_client->didFetchDataLoadedFormData(m_formData); |
| + return; |
| + } |
| + result = BytesConsumer::Result::Error; |
| + } |
| + switch (result) { |
| + case BytesConsumer::Result::Ok: |
| + break; |
| + case BytesConsumer::Result::ShouldWait: |
| + case BytesConsumer::Result::Done: |
| + NOTREACHED(); |
| + return; |
| + case BytesConsumer::Result::Error: |
| + cancel(); |
| + 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 partHeaderFieldsInMultipartReceived(const ResourceResponse& response) override |
| + { |
| + if (!m_currentEntry.initialize(response)) |
| + m_multipartParser->cancel(); |
| + } |
| + |
| + void partDataInMultipartReceived(const char* bytes, size_t size) override |
| + { |
| + if (!m_currentEntry.appendBytes(bytes, size)) |
| + m_multipartParser->cancel(); |
| + } |
| + |
| + void partDataInMultipartFullyReceived() override |
| + { |
| + if (!m_currentEntry.finish(m_formData)) |
| + m_multipartParser->cancel(); |
| + } |
| + |
| + class Entry { |
| + public: |
| + bool initialize(const ResourceResponse& response) |
| + { |
| + ParsedContentType disposition(response.httpHeaderField(HTTPNames::Content_Disposition)); |
|
horo
2016/09/27 10:45:50
nit: const
e_hakkinen
2016/09/28 15:15:04
Done.
|
| + String dispositionType = disposition.mimeType(); |
|
horo
2016/09/27 10:45:49
nit: const
e_hakkinen
2016/09/28 15:15:04
Done.
|
| + 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 { |
|
yhirano
2016/09/26 10:59:34
You're assuming that each part which doesn't have
e_hakkinen
2016/09/28 15:15:04
I am not sure as https://fetch.spec.whatwg.org/#co
yhirano
2016/09/29 08:30:09
If the spec is unclear, I think it's a good idea t
|
| + if (!m_decoder) |
| + m_decoder = TextResourceDecoder::createAlwaysUseUTF8ForText(); |
|
yhirano
2016/09/26 10:59:34
Is always using utf-8 appropriate? https://tools.i
e_hakkinen
2016/09/28 15:15:04
I think so.
The https://fetch.spec.whatwg.org/#co
|
| + 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(); |
|
horo
2016/09/27 10:45:49
nit: const
e_hakkinen
2016/09/28 15:15:04
Done.
|
| + 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 +494,11 @@ FetchDataLoader* FetchDataLoader::createLoaderAsArrayBuffer() |
| return new FetchDataLoaderAsArrayBuffer(); |
| } |
| +FetchDataLoader* FetchDataLoader::createLoaderAsFormData(const String& multipartBoundary) |
| +{ |
| + return new FetchDataLoaderAsFormData(multipartBoundary); |
| +} |
| + |
| FetchDataLoader* FetchDataLoader::createLoaderAsString() |
| { |
| return new FetchDataLoaderAsString(); |