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(); |
| } |