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 ac601a3b59e6087428c7204e09ad12a236334bb9..54ac42dc68bbcffbda657b2cefd44b5da05cf339 100644 | 
| --- a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp | 
| +++ b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp | 
| @@ -5,9 +5,14 @@ | 
| #include "modules/fetch/FetchDataLoader.h" | 
| #include <memory> | 
| +#include "core/fileapi/File.h" | 
| +#include "core/html/FormData.h" | 
| #include "core/html/parser/TextResourceDecoder.h" | 
| #include "modules/fetch/BytesConsumer.h" | 
| +#include "modules/fetch/MultipartParser.h" | 
| #include "mojo/public/cpp/system/simple_watcher.h" | 
| +#include "platform/HTTPNames.h" | 
| +#include "platform/network/ParsedContentType.h" | 
| #include "platform/wtf/Functional.h" | 
| #include "platform/wtf/PtrUtil.h" | 
| #include "platform/wtf/text/StringBuilder.h" | 
| @@ -170,6 +175,177 @@ class FetchDataLoaderAsArrayBuffer final : public FetchDataLoader, | 
| std::unique_ptr<ArrayBufferBuilder> raw_data_; | 
| }; | 
| +class FetchDataLoaderAsFormData final : public FetchDataLoader, | 
| + public BytesConsumer::Client, | 
| + public MultipartParser::Client { | 
| + USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsFormData); | 
| + | 
| + public: | 
| + explicit FetchDataLoaderAsFormData(const String& multipart_boundary) | 
| + : multipart_boundary_(multipart_boundary) {} | 
| + | 
| + void Start(BytesConsumer* consumer, | 
| + FetchDataLoader::Client* client) override { | 
| + DCHECK(!client_); | 
| + DCHECK(!consumer_); | 
| + DCHECK(!form_data_); | 
| + DCHECK(!multipart_parser_); | 
| + | 
| + const CString multipart_boundary_utf8 = multipart_boundary_.Utf8(); | 
| + Vector<char> multipart_boundary_vector; | 
| + multipart_boundary_vector.Append(multipart_boundary_utf8.data(), | 
| + multipart_boundary_utf8.length()); | 
| + | 
| + client_ = client; | 
| + form_data_ = FormData::Create(); | 
| + multipart_parser_ = | 
| + new MultipartParser(std::move(multipart_boundary_vector), this); | 
| + consumer_ = consumer; | 
| + consumer_->SetClient(this); | 
| + OnStateChange(); | 
| + } | 
| + | 
| + void OnStateChange() override { | 
| + while (true) { | 
| + const char* buffer; | 
| + size_t available; | 
| + auto result = consumer_->BeginRead(&buffer, &available); | 
| + if (result == BytesConsumer::Result::kShouldWait) | 
| + return; | 
| + if (result == BytesConsumer::Result::kOk) { | 
| + const bool buffer_appended = | 
| + multipart_parser_->AppendData(buffer, available); | 
| + const bool multipart_receive_failed = multipart_parser_->IsCancelled(); | 
| + result = consumer_->EndRead(available); | 
| + if (!buffer_appended || multipart_receive_failed) | 
| + result = BytesConsumer::Result::kError; | 
| + } | 
| + switch (result) { | 
| + case BytesConsumer::Result::kOk: | 
| + break; | 
| + case BytesConsumer::Result::kShouldWait: | 
| + NOTREACHED(); | 
| + return; | 
| + case BytesConsumer::Result::kDone: | 
| + if (multipart_parser_->Finish()) { | 
| + DCHECK(!multipart_parser_->IsCancelled()); | 
| + client_->DidFetchDataLoadedFormData(form_data_); | 
| + } else { | 
| + client_->DidFetchDataLoadFailed(); | 
| + } | 
| + return; | 
| + case BytesConsumer::Result::kError: | 
| + client_->DidFetchDataLoadFailed(); | 
| + return; | 
| + } | 
| + } | 
| + } | 
| + | 
| + void Cancel() override { | 
| + consumer_->Cancel(); | 
| + multipart_parser_->Cancel(); | 
| + } | 
| + | 
| + DEFINE_INLINE_TRACE() { | 
| + visitor->Trace(consumer_); | 
| + visitor->Trace(client_); | 
| + visitor->Trace(form_data_); | 
| + visitor->Trace(multipart_parser_); | 
| + FetchDataLoader::Trace(visitor); | 
| + BytesConsumer::Client::Trace(visitor); | 
| + MultipartParser::Client::Trace(visitor); | 
| + } | 
| + | 
| + private: | 
| + void PartHeaderFieldsInMultipartReceived( | 
| + const HTTPHeaderMap& header_fields) override { | 
| + if (!current_entry_.Initialize(header_fields)) | 
| + multipart_parser_->Cancel(); | 
| + } | 
| + | 
| + void PartDataInMultipartReceived(const char* bytes, size_t size) override { | 
| + if (!current_entry_.AppendBytes(bytes, size)) | 
| + multipart_parser_->Cancel(); | 
| + } | 
| + | 
| + void PartDataInMultipartFullyReceived() override { | 
| + if (!current_entry_.Finish(form_data_)) | 
| + multipart_parser_->Cancel(); | 
| + } | 
| + | 
| + class Entry { | 
| + public: | 
| + bool Initialize(const HTTPHeaderMap& header_fields) { | 
| + const ParsedContentType disposition( | 
| 
 
yhirano
2017/05/10 05:59:36
Need some comments why we use x-fake.
 
e_hakkinen
2017/05/11 11:01:51
Done.
I also created https://crrev.com/2880543003
 
 | 
| + "x-fake/" + header_fields.Get(HTTPNames::Content_Disposition)); | 
| + const String disposition_type = disposition.MimeType().Substring(7u); | 
| 
 
yhirano
2017/05/10 05:59:36
Need some comments for the magic number.
 
e_hakkinen
2017/05/11 11:01:51
Done.
 
 | 
| + filename_ = disposition.ParameterValueForName("filename"); | 
| + name_ = disposition.ParameterValueForName("name"); | 
| + blob_data_.reset(); | 
| + string_builder_.reset(); | 
| + if (disposition_type != "form-data" || name_.IsNull()) | 
| + return false; | 
| + if (!filename_.IsNull()) { | 
| + blob_data_ = BlobData::Create(); | 
| + const AtomicString& content_type = | 
| + header_fields.Get(HTTPNames::Content_Type); | 
| + blob_data_->SetContentType(content_type.IsNull() ? "text/plain" | 
| + : content_type); | 
| + } else { | 
| + if (!string_decoder_) | 
| + string_decoder_ = TextResourceDecoder::CreateAlwaysUseUTF8ForText(); | 
| + string_builder_.reset(new StringBuilder); | 
| + } | 
| + return true; | 
| + } | 
| + | 
| + bool AppendBytes(const char* bytes, size_t size) { | 
| + if (blob_data_) | 
| + blob_data_->AppendBytes(bytes, size); | 
| + if (string_builder_) { | 
| + string_builder_->Append(string_decoder_->Decode(bytes, size)); | 
| + if (string_decoder_->SawError()) | 
| + return false; | 
| + } | 
| + return true; | 
| + } | 
| + | 
| + bool Finish(FormData* form_data) { | 
| + if (blob_data_) { | 
| + DCHECK(!string_builder_); | 
| + const auto size = blob_data_->length(); | 
| + File* file = | 
| + File::Create(filename_, InvalidFileTime(), | 
| + BlobDataHandle::Create(std::move(blob_data_), size)); | 
| + form_data->append(name_, file, filename_); | 
| + } | 
| + if (string_builder_) { | 
| + DCHECK(!blob_data_); | 
| + string_builder_->Append(string_decoder_->Flush()); | 
| + if (string_decoder_->SawError()) | 
| + return false; | 
| + form_data->append(name_, string_builder_->ToString()); | 
| + } | 
| + return true; | 
| + } | 
| + | 
| + private: | 
| + std::unique_ptr<BlobData> blob_data_; | 
| + String filename_; | 
| + String name_; | 
| + std::unique_ptr<StringBuilder> string_builder_; | 
| + std::unique_ptr<TextResourceDecoder> string_decoder_; | 
| + }; | 
| + | 
| + Member<BytesConsumer> consumer_; | 
| + Member<FetchDataLoader::Client> client_; | 
| + Member<FormData> form_data_; | 
| + Member<MultipartParser> multipart_parser_; | 
| + | 
| + Entry current_entry_; | 
| + String multipart_boundary_; | 
| +}; | 
| + | 
| class FetchDataLoaderAsString final : public FetchDataLoader, | 
| public BytesConsumer::Client { | 
| USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsString); | 
| @@ -341,6 +517,11 @@ FetchDataLoader* FetchDataLoader::CreateLoaderAsArrayBuffer() { | 
| return new FetchDataLoaderAsArrayBuffer(); | 
| } | 
| +FetchDataLoader* FetchDataLoader::CreateLoaderAsFormData( | 
| + const String& multipartBoundary) { | 
| + return new FetchDataLoaderAsFormData(multipartBoundary); | 
| +} | 
| + | 
| FetchDataLoader* FetchDataLoader::CreateLoaderAsString() { | 
| return new FetchDataLoaderAsString(); | 
| } |