Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4671)

Unified Diff: Source/modules/fetch/FetchFormDataConsumerHandle.cpp

Issue 1265413002: Introduce FetchFormDataConsumerHandle. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Add (const void*, size_t) creation function Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/modules/fetch/FetchFormDataConsumerHandle.cpp
diff --git a/Source/modules/fetch/FetchFormDataConsumerHandle.cpp b/Source/modules/fetch/FetchFormDataConsumerHandle.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..71cb4ec583ad795edcbc9c4a66ce61126ce45ed9
--- /dev/null
+++ b/Source/modules/fetch/FetchFormDataConsumerHandle.cpp
@@ -0,0 +1,277 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "modules/fetch/FetchFormDataConsumerHandle.h"
+
+#include "modules/fetch/DataConsumerHandleUtil.h"
+#include "modules/fetch/FetchBlobDataConsumerHandle.h"
+#include "wtf/ThreadSafeRefCounted.h"
+#include "wtf/ThreadingPrimitives.h"
+#include "wtf/Vector.h"
+#include "wtf/text/TextCodec.h"
+#include "wtf/text/TextEncoding.h"
+#include "wtf/text/WTFString.h"
+
+#include <utility>
+
+namespace blink {
+
+using Result = FetchDataConsumerHandle::Result;
+
+class FetchFormDataConsumerHandle::Context : public ThreadSafeRefCounted<Context> {
+ WTF_MAKE_NONCOPYABLE(Context);
+public:
+ virtual ~Context() {}
+ virtual PassOwnPtr<FetchDataConsumerHandle::Reader> obtainReader(Client*) = 0;
+
+protected:
+ explicit Context() {}
+};
+
+class FetchFormDataConsumerHandle::SimpleContext final : public Context {
+ class ReaderImpl;
+public:
+ static PassRefPtr<SimpleContext> create(const String& body) { return adoptRef(new SimpleContext(body)); }
+ static PassRefPtr<SimpleContext> create(const void* data, size_t size) { return adoptRef(new SimpleContext(data, size)); }
+ static PassRefPtr<SimpleContext> create(PassRefPtr<FormData> body) { return adoptRef(new SimpleContext(body)); }
+
+ PassOwnPtr<Reader> obtainReader(Client* client) override
+ {
+ // For memory barrier.
+ Mutex m;
+ MutexLocker locker(m);
hiroshige 2015/08/11 09:35:01 BTW if we need memory barrier here, should we also
yhirano 2015/08/11 11:36:01 If a user obtains and releases a reader on one thr
+ return ReaderImpl::create(this, client);
+ }
+
+ PassRefPtr<FormData> drainFormData()
hiroshige 2015/08/11 09:35:01 IIUC: 1. If flatten() is called before drainFormDa
yhirano 2015/08/11 11:36:01 Done.
+ {
+ ASSERT(!m_formData || m_formData->hasOneRef());
+ return m_formData.release();
+ }
+
+ Result read(void* data, size_t size, Flags flags, size_t* readSize)
+ {
+ *readSize = 0;
+ if (size == 0) {
+ if (!m_formData && m_flattenFormData.size() == m_flattenFormDataOffset)
+ return WebDataConsumerHandle::Done;
+ return WebDataConsumerHandle::Ok;
+ }
+ flatten();
+
hiroshige 2015/08/11 09:35:01 How about adding "RELEASE_ASSERT(m_flattenFormData
yhirano 2015/08/11 11:36:01 Done.
+ *readSize = std::min(size, m_flattenFormData.size() - m_flattenFormDataOffset);
+ if (*readSize == 0)
+ return WebDataConsumerHandle::Done;
+ memcpy(data, &m_flattenFormData[m_flattenFormDataOffset], *readSize);
+ m_flattenFormDataOffset += *readSize;
+ return WebDataConsumerHandle::Ok;
+ }
+
+ Result beginRead(const void** buffer, Flags flags, size_t* available)
+ {
+ *buffer = nullptr;
+ *available = 0;
+
+ flatten();
+ if (m_flattenFormData.size() == m_flattenFormDataOffset)
+ return WebDataConsumerHandle::Done;
hiroshige 2015/08/11 09:35:01 ditto.
yhirano 2015/08/11 11:36:01 Done.
+ *buffer = &m_flattenFormData[m_flattenFormDataOffset];
+ *available = m_flattenFormData.size() - m_flattenFormDataOffset;
+ return WebDataConsumerHandle::Ok;
+ }
+
+ Result endRead(size_t read)
+ {
hiroshige 2015/08/11 09:35:01 Please add: RELEASE_ASSERT(m_flattenFormData.s
yhirano 2015/08/11 11:36:01 Done.
+ m_flattenFormDataOffset += read;
+ return WebDataConsumerHandle::Ok;
+ }
+
+private:
+ class ReaderImpl final : public FetchDataConsumerHandle::Reader {
+ WTF_MAKE_NONCOPYABLE(ReaderImpl);
+ public:
+ static PassOwnPtr<ReaderImpl> create(PassRefPtr<SimpleContext> context, Client* client) { return adoptPtr(new ReaderImpl(context, client)); }
+ Result read(void* data, size_t size, Flags flags, size_t* readSize) override
+ {
+ return m_context->read(data, size, flags, readSize);
+ }
+ Result beginRead(const void** buffer, Flags flags, size_t* available) override
+ {
+ return m_context->beginRead(buffer, flags, available);
+ }
+ Result endRead(size_t read) override
+ {
+ return m_context->endRead(read);
+ }
+ PassRefPtr<FormData> drainAsFormData() override
+ {
+ return m_context->drainFormData();
+ }
+
+ private:
+ ReaderImpl(PassRefPtr<SimpleContext> context, Client* client) : m_context(context), m_notifier(client) {}
+
+ RefPtr<SimpleContext> m_context;
+ NotifyOnReaderCreationHelper m_notifier;
+ };
+
+ explicit SimpleContext(const String& body)
+ : m_formData(FormData::create(UTF8Encoding().encode(body, WTF::EntitiesForUnencodables)))
+ , m_flattenFormDataOffset(0) {}
+ explicit SimpleContext(const void* data, size_t size)
+ : m_formData(FormData::create(data, size))
+ , m_flattenFormDataOffset(0) {}
+ explicit SimpleContext(PassRefPtr<FormData> body)
+ : m_formData(body->deepCopy())
+ , m_flattenFormDataOffset(0) {}
+
+ void flatten()
+ {
+ if (!m_formData) {
+ // It is already drained or flatten.
+ return;
+ }
+ ASSERT(m_formData->hasOneRef());
+ m_formData->flatten(m_flattenFormData);
+ m_formData = nullptr;
+ }
+
+ RefPtr<FormData> m_formData;
+ Vector<char> m_flattenFormData;
+ size_t m_flattenFormDataOffset;
+};
+
+class FetchFormDataConsumerHandle::ComplexContext final : public Context {
+ class ReaderImpl;
+public:
+ static PassRefPtr<ComplexContext> create(ExecutionContext* executionContext,
+ PassRefPtr<FormData> formData,
+ FetchBlobDataConsumerHandle::LoaderFactory* factory)
+ {
+ return adoptRef(new ComplexContext(executionContext, formData, factory));
+ }
+
+ PassOwnPtr<FetchFormDataConsumerHandle::Reader> obtainReader(Client* client) override
+ {
+ // For memory barrier.
+ Mutex m;
+ MutexLocker locker(m);
+ return ReaderImpl::create(this, client);
+ }
+
+private:
+ class ReaderImpl final : public FetchDataConsumerHandle::Reader {
+ WTF_MAKE_NONCOPYABLE(ReaderImpl);
+ public:
+ static PassOwnPtr<ReaderImpl> create(PassRefPtr<ComplexContext> context, Client* client) { return adoptPtr(new ReaderImpl(context, client)); }
+ Result read(void* data, size_t size, Flags flags, size_t* readSize) override
+ {
+ Result r = m_reader->read(data, size, flags, readSize);
+ if (!((r == Ok && *readSize == 0) || r == ShouldWait)) {
+ m_context->drainFormData();
+ }
+ return r;
+ }
+ Result beginRead(const void** buffer, Flags flags, size_t* available) override
+ {
+ Result r = m_reader->beginRead(buffer, flags, available);
+ if (r != ShouldWait) {
+ m_context->drainFormData();
+ }
+ return r;
+ }
+ Result endRead(size_t read) override
+ {
+ return m_reader->endRead(read);
+ }
+ PassRefPtr<BlobDataHandle> drainAsBlobDataHandle(BlobSizePolicy policy) override
+ {
+ m_context->drainFormData();
+ return m_reader->drainAsBlobDataHandle();
+ }
+ PassRefPtr<FormData> drainAsFormData() override
+ {
+ // Drain from the internal reader to sync the state.
+ m_reader->drainAsFormData();
+ return m_context->drainFormData();
+ }
+ private:
+ ReaderImpl(PassRefPtr<ComplexContext> context, Client* client) : m_context(context), m_reader(m_context->m_handle->obtainReader(client)) {}
+
+ RefPtr<ComplexContext> m_context;
+ OwnPtr<FetchDataConsumerHandle::Reader> m_reader;
+ };
+
+ explicit ComplexContext(ExecutionContext* executionContext, PassRefPtr<FormData> body, FetchBlobDataConsumerHandle::LoaderFactory* factory)
+ : m_formData(body->deepCopy())
+ {
+ OwnPtr<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, 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);
hiroshige 2015/08/11 10:19:04 This is not thread-safe, because appendFileSystemU
yhirano 2015/08/11 11:36:01 Nice catch! done.
+ break;
+ }
+ }
+ // Here we handle m_formData->boundary() as a C-style string. See
+ // FormDataBuilder::generateUniqueBoundaryString.
+ blobData->setContentType(AtomicString("multipart/form-data; boundary=", AtomicString::ConstructFromLiteral) + m_formData->boundary().data());
+ auto size = blobData->length();
+ if (factory) {
+ // For testing
+ m_handle = FetchBlobDataConsumerHandle::create(executionContext, BlobDataHandle::create(blobData.release(), size), factory);
+ } else {
+ m_handle = FetchBlobDataConsumerHandle::create(executionContext, BlobDataHandle::create(blobData.release(), size));
+ }
+ }
+ PassRefPtr<FormData> drainFormData()
+ {
+ ASSERT(!m_formData || m_formData->hasOneRef());
+ return m_formData.release();
+ }
+
+ RefPtr<FormData> m_formData;
+ OwnPtr<FetchDataConsumerHandle> m_handle;
+};
+
+FetchFormDataConsumerHandle::FetchFormDataConsumerHandle(const String& body) : m_context(SimpleContext::create(body)) {}
+FetchFormDataConsumerHandle::FetchFormDataConsumerHandle(const void* data, size_t size) : m_context(SimpleContext::create(data, size)) {}
+FetchFormDataConsumerHandle::FetchFormDataConsumerHandle(ExecutionContext* executionContext,
+ PassRefPtr<FormData> body,
+ FetchBlobDataConsumerHandle::LoaderFactory* loaderFactory)
+{
+ if (isSimple(body.get())) {
+ m_context = SimpleContext::create(body);
+ } else {
+ m_context = ComplexContext::create(executionContext, body, loaderFactory);
+ }
+}
+FetchFormDataConsumerHandle::~FetchFormDataConsumerHandle() {}
+
+FetchDataConsumerHandle::Reader* FetchFormDataConsumerHandle::obtainReaderInternal(Client* client)
+{
+ return m_context->obtainReader(client).leakPtr();
+}
+
+bool FetchFormDataConsumerHandle::isSimple(const FormData* formData)
+{
+ for (const auto& element : formData->elements()) {
+ if (element.m_type != FormDataElement::data)
+ return false;
+ }
+ return true;
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698