Chromium Code Reviews| Index: third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp |
| diff --git a/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp b/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp |
| index 0e36d2d7df54c02992f8a87ea94caded250b6b30..96de7535cf8d3025ae9f20e975789d4cd6fbf273 100644 |
| --- a/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp |
| +++ b/third_party/WebKit/Source/modules/fetch/FormDataBytesConsumer.cpp |
| @@ -4,8 +4,13 @@ |
| #include "modules/fetch/FormDataBytesConsumer.h" |
| +#include "bindings/core/v8/ExceptionState.h" |
| #include "core/dom/DOMArrayBuffer.h" |
| #include "core/dom/DOMArrayBufferView.h" |
| +#include "core/dom/ExecutionContext.h" |
| +#include "core/dom/TaskRunnerHelper.h" |
| +#include "core/fileapi/Blob.h" |
| +#include "core/fileapi/File.h" |
| #include "modules/fetch/BlobBytesConsumer.h" |
| #include "platform/blob/BlobData.h" |
| #include "platform/network/EncodedFormData.h" |
| @@ -18,6 +23,8 @@ namespace blink { |
| namespace { |
| +using BytesConsumerFactory = FormDataBytesConsumer::BytesConsumerFactory; |
| + |
| bool isSimple(const EncodedFormData* formData) { |
| for (const auto& element : formData->elements()) { |
| if (element.m_type != FormDataElement::data) |
| @@ -101,96 +108,204 @@ class SimpleFormDataBytesConsumer : public BytesConsumer { |
| }; |
| class ComplexFormDataBytesConsumer final : public BytesConsumer { |
| + enum class State { |
| + Clean, |
| + Reading, |
| + Closed, |
| + Errored, |
| + }; |
| + |
| public: |
| ComplexFormDataBytesConsumer(ExecutionContext* executionContext, |
| PassRefPtr<EncodedFormData> formData, |
| - BytesConsumer* consumer) |
| - : m_formData(formData) { |
| - if (consumer) { |
| - // For testing. |
| - m_blobBytesConsumer = consumer; |
| - return; |
| - } |
| + std::unique_ptr<BytesConsumerFactory> factory) |
| + : m_formData(formData), |
| + m_executionContext(executionContext), |
| + m_factoryForTesting(std::move(factory)) {} |
| - std::unique_ptr<BlobData> blobData = BlobData::create(); |
| - for (const auto& element : m_formData->elements()) { |
| - switch (element.m_type) { |
| - case FormDataElement::data: |
| - blobData->appendBytes(element.m_data.data(), element.m_data.size()); |
| - break; |
| - case FormDataElement::encodedFile: |
| - blobData->appendFile(element.m_filename, element.m_fileStart, |
| - element.m_fileLength, |
|
dmurph
2017/02/24 19:49:24
Why not just check if the file size is -1, and if
yhirano
2017/03/02 04:05:04
Fixed.
|
| - element.m_expectedFileModificationTime); |
| - break; |
| - case FormDataElement::encodedBlob: |
| - if (element.m_optionalBlobDataHandle) |
| - blobData->appendBlob(element.m_optionalBlobDataHandle, 0, |
| - element.m_optionalBlobDataHandle->size()); |
| - break; |
| - case FormDataElement::encodedFileSystemURL: |
| - blobData->appendFileSystemURL( |
| - element.m_fileSystemURL, element.m_fileStart, |
| - element.m_fileLength, element.m_expectedFileModificationTime); |
| - break; |
| + // BytesConsumer implementation |
| + Result beginRead(const char** buffer, size_t* available) override { |
| + *buffer = nullptr; |
| + *available = 0; |
| + if (m_state == State::Closed) |
| + return Result::Done; |
| + if (m_state == State::Errored) |
| + return Result::Error; |
| + if (m_state == State::Clean) |
| + m_state = State::Reading; |
| + |
| + if (m_blobBytesConsumer) { |
| + auto r = m_blobBytesConsumer->beginRead(buffer, available); |
| + if (r == Result::Error) { |
| + m_state = State::Errored; |
| + return r; |
| } |
| + if (r == Result::Done) { |
| + ++m_elementIndex; |
| + m_blobBytesConsumer = nullptr; |
| + if (m_elementIndex == m_formData->elements().size()) { |
| + m_state = State::Closed; |
| + return Result::Done; |
| + } |
| + return beginRead(buffer, available); |
| + } |
| + return r; |
| + } |
| + const auto& element = m_formData->elements()[m_elementIndex]; |
| + if (element.m_type == FormDataElement::data) { |
| + DCHECK_LE(m_offset, element.m_data.size()); |
| + *buffer = &element.m_data[m_offset]; |
| + *available = element.m_data.size() - m_offset; |
| + return Result::Ok; |
| } |
| - // Here we handle m_formData->boundary() as a C-style string. See |
| - // FormDataEncoder::generateUniqueBoundaryString. |
| - blobData->setContentType(AtomicString("multipart/form-data; boundary=") + |
| - m_formData->boundary().data()); |
| - auto size = blobData->length(); |
| - m_blobBytesConsumer = new BlobBytesConsumer( |
| - executionContext, BlobDataHandle::create(std::move(blobData), size)); |
| - } |
| - // BytesConsumer implementation |
| - Result beginRead(const char** buffer, size_t* available) override { |
| - m_formData = nullptr; |
| - // Delegate the operation to the underlying consumer. This relies on |
| - // the fact that we appropriately notify the draining information to |
| - // the underlying consumer. |
| - return m_blobBytesConsumer->beginRead(buffer, available); |
| + RefPtr<BlobDataHandle> blobDataHandle = nullptr; |
| + const String type = "application/octet-stream"; |
| + switch (element.m_type) { |
| + case FormDataElement::data: |
| + NOTREACHED(); |
| + break; |
| + case FormDataElement::encodedBlob: |
| + if (element.m_optionalBlobDataHandle) |
| + blobDataHandle = element.m_optionalBlobDataHandle; |
| + else |
| + blobDataHandle = BlobDataHandle::create(element.m_blobUUID, type, -1); |
| + break; |
| + case FormDataElement::encodedFile: { |
| + NonThrowableExceptionState es; |
| + Blob* file = File::create(element.m_filename); |
| + if (element.m_fileStart > 0) { |
| + file = |
| + file->slice(element.m_fileStart, |
| + element.m_fileStart + element.m_fileLength, type, es); |
| + } |
| + blobDataHandle = file->blobDataHandle(); |
| + break; |
| + } |
| + case FormDataElement::encodedFileSystemURL: { |
| + FileMetadata metadata; |
| + metadata.length = element.m_fileStart + element.m_fileLength; |
| + NonThrowableExceptionState es; |
| + Blob* file = File::createForFileSystemFile( |
| + element.m_fileSystemURL, metadata, File::IsUserVisible); |
| + if (element.m_fileStart > 0) { |
| + file = |
| + file->slice(element.m_fileStart, |
| + element.m_fileStart + element.m_fileLength, type, es); |
| + } |
| + blobDataHandle = file->blobDataHandle(); |
| + break; |
| + } |
| + } |
| + if (m_factoryForTesting) { |
| + m_blobBytesConsumer = |
| + (*m_factoryForTesting)(m_executionContext, std::move(blobDataHandle)); |
| + } else { |
| + m_blobBytesConsumer = |
| + new BlobBytesConsumer(m_executionContext, std::move(blobDataHandle)); |
| + } |
| + if (m_client) |
| + m_blobBytesConsumer->setClient(m_client); |
| + return beginRead(buffer, available); |
| } |
| Result endRead(size_t readSize) override { |
| - return m_blobBytesConsumer->endRead(readSize); |
| - } |
| - PassRefPtr<BlobDataHandle> drainAsBlobDataHandle( |
| - BlobSizePolicy policy) override { |
| - RefPtr<BlobDataHandle> handle = |
| - m_blobBytesConsumer->drainAsBlobDataHandle(policy); |
| - if (handle) |
| - m_formData = nullptr; |
| - return handle.release(); |
| + DCHECK_EQ(m_state, State::Reading); |
| + const auto& element = m_formData->elements()[m_elementIndex]; |
| + if (element.m_type == FormDataElement::data) { |
| + DCHECK_LE(m_offset + readSize, element.m_data.size()); |
| + m_offset += readSize; |
| + if (m_offset == element.m_data.size()) { |
| + m_offset = 0; |
| + ++m_elementIndex; |
| + } |
| + } else { |
| + DCHECK(m_blobBytesConsumer); |
| + auto r = m_blobBytesConsumer->endRead(readSize); |
| + if (r == Result::Error) { |
| + m_state = State::Errored; |
| + return r; |
| + } |
| + if (r == Result::Done) { |
| + m_blobBytesConsumer = nullptr; |
| + ++m_elementIndex; |
| + } |
| + } |
| + if (m_elementIndex == m_formData->elements().size()) { |
| + m_state = State::Closed; |
| + return Result::Done; |
| + } |
| + return Result::Ok; |
| } |
| PassRefPtr<EncodedFormData> drainAsFormData() override { |
| - if (!m_formData) |
| + if (m_state != State::Clean) |
| return nullptr; |
| - m_blobBytesConsumer->cancel(); |
| + DCHECK(!m_blobBytesConsumer); |
| + DCHECK(m_formData); |
| + m_state = State::Closed; |
| return m_formData.release(); |
| } |
| void setClient(BytesConsumer::Client* client) override { |
| - m_blobBytesConsumer->setClient(client); |
| + DCHECK(client); |
| + m_client = client; |
| + if (m_blobBytesConsumer) { |
| + m_blobBytesConsumer->setClient(client); |
| + } else if (getPublicState() == PublicState::ReadableOrWaiting) { |
| + TaskRunnerHelper::get(TaskType::Networking, m_executionContext) |
| + ->postTask(BLINK_FROM_HERE, |
| + WTF::bind(&BytesConsumer::Client::onStateChange, |
| + wrapPersistent(m_client.get()))); |
| + } |
| + } |
| + void clearClient() override { |
| + m_client = nullptr; |
| + if (m_blobBytesConsumer) |
| + m_blobBytesConsumer->clearClient(); |
| } |
| - void clearClient() override { m_blobBytesConsumer->clearClient(); } |
| void cancel() override { |
| + if (m_state == State::Closed || m_state == State::Errored) |
| + return; |
| m_formData = nullptr; |
| - m_blobBytesConsumer->cancel(); |
| + m_client = nullptr; |
| + m_state = State::Closed; |
| + if (m_blobBytesConsumer) |
| + m_blobBytesConsumer->cancel(); |
| } |
| PublicState getPublicState() const override { |
| - return m_blobBytesConsumer->getPublicState(); |
| + switch (m_state) { |
| + case State::Clean: |
| + case State::Reading: |
| + return PublicState::ReadableOrWaiting; |
| + case State::Closed: |
| + return PublicState::Closed; |
| + case State::Errored: |
| + return PublicState::Errored; |
| + } |
| + NOTREACHED(); |
| + return PublicState::Errored; |
| + } |
| + Error getError() const override { |
| + if (m_blobBytesConsumer) |
| + return m_blobBytesConsumer->getError(); |
| + return Error(); |
| } |
| - Error getError() const override { return m_blobBytesConsumer->getError(); } |
| String debugName() const override { return "ComplexFormDataBytesConsumer"; } |
| DEFINE_INLINE_TRACE() { |
| visitor->trace(m_blobBytesConsumer); |
| + visitor->trace(m_client); |
| + visitor->trace(m_executionContext); |
| BytesConsumer::trace(visitor); |
| } |
| private: |
| + unsigned m_elementIndex = 0; |
| + size_t m_offset = 0; |
| + State m_state = State::Clean; |
| RefPtr<EncodedFormData> m_formData; |
| Member<BytesConsumer> m_blobBytesConsumer; |
| + Member<BytesConsumer::Client> m_client; |
| + Member<ExecutionContext> m_executionContext; |
| + std::unique_ptr<BytesConsumerFactory> m_factoryForTesting; |
| }; |
| } // namespace |
| @@ -217,13 +332,13 @@ FormDataBytesConsumer::FormDataBytesConsumer( |
| FormDataBytesConsumer::FormDataBytesConsumer( |
| ExecutionContext* executionContext, |
| PassRefPtr<EncodedFormData> formData, |
| - BytesConsumer* consumer) |
| + std::unique_ptr<BytesConsumerFactory> factory) |
| : m_impl(isSimple(formData.get()) |
| ? static_cast<BytesConsumer*>( |
| new SimpleFormDataBytesConsumer(std::move(formData))) |
| : static_cast<BytesConsumer*>( |
| new ComplexFormDataBytesConsumer(executionContext, |
| std::move(formData), |
| - consumer))) {} |
| + std::move(factory)))) {} |
| } // namespace blink |