| 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..abc77dcac7631020bf79cbdf2569e751c5a55060 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,175 @@ 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();
|
| + 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();
|
| + }
|
| + m_consumer->endRead(available);
|
| + 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 +493,11 @@ FetchDataLoader* FetchDataLoader::createLoaderAsArrayBuffer()
|
| return new FetchDataLoaderAsArrayBuffer();
|
| }
|
|
|
| +FetchDataLoader* FetchDataLoader::createLoaderAsFormData(const String& multipartBoundary)
|
| +{
|
| + return new FetchDataLoaderAsFormData(multipartBoundary);
|
| +}
|
| +
|
| FetchDataLoader* FetchDataLoader::createLoaderAsString()
|
| {
|
| return new FetchDataLoaderAsString();
|
|
|