Commit e3820506 authored by yhirano's avatar yhirano Committed by Commit bot

Implement BlobBytesConsumer

This CL implements BlobBytesConsumer which will replace
FetchBlobDataConsumerHandle.

BUG=610195

Review-Url: https://codereview.chromium.org/2287323002
Cr-Commit-Position: refs/heads/master@{#421170}
parent 87cd0848
...@@ -227,6 +227,7 @@ source_set("unit_tests") { ...@@ -227,6 +227,7 @@ source_set("unit_tests") {
"credentialmanager/PasswordCredentialTest.cpp", "credentialmanager/PasswordCredentialTest.cpp",
"csspaint/PaintRenderingContext2DTest.cpp", "csspaint/PaintRenderingContext2DTest.cpp",
"csspaint/PaintWorkletTest.cpp", "csspaint/PaintWorkletTest.cpp",
"fetch/BlobBytesConsumerTest.cpp",
"fetch/BodyStreamBufferTest.cpp", "fetch/BodyStreamBufferTest.cpp",
"fetch/BytesConsumerForDataConsumerHandleTest.cpp", "fetch/BytesConsumerForDataConsumerHandleTest.cpp",
"fetch/BytesConsumerTest.cpp", "fetch/BytesConsumerTest.cpp",
......
...@@ -6,6 +6,8 @@ import("//third_party/WebKit/Source/modules/modules.gni") ...@@ -6,6 +6,8 @@ import("//third_party/WebKit/Source/modules/modules.gni")
blink_modules_sources("fetch") { blink_modules_sources("fetch") {
sources = [ sources = [
"BlobBytesConsumer.cpp",
"BlobBytesConsumer.h",
"Body.cpp", "Body.cpp",
"Body.h", "Body.h",
"BodyStreamBuffer.cpp", "BodyStreamBuffer.cpp",
......
// Copyright 2016 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 "modules/fetch/BlobBytesConsumer.h"
#include "core/fetch/FetchInitiatorTypeNames.h"
#include "core/loader/ThreadableLoader.h"
#include "modules/fetch/BytesConsumerForDataConsumerHandle.h"
#include "modules/fetch/DataConsumerHandleUtil.h"
#include "platform/blob/BlobData.h"
#include "platform/blob/BlobRegistry.h"
#include "platform/blob/BlobURL.h"
#include "platform/network/ResourceError.h"
#include "platform/network/ResourceRequest.h"
#include "platform/weborigin/KURL.h"
#include "platform/weborigin/SecurityOrigin.h"
namespace blink {
BlobBytesConsumer::BlobBytesConsumer(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle, ThreadableLoader* loader)
: ContextLifecycleObserver(executionContext)
, m_blobDataHandle(blobDataHandle)
, m_loader(loader)
{
ThreadState::current()->registerPreFinalizer(this);
if (!m_blobDataHandle) {
// Note that |m_loader| is non-null only in tests.
if (m_loader) {
m_loader->cancel();
m_loader = nullptr;
}
m_state = PublicState::Closed;
}
}
BlobBytesConsumer::BlobBytesConsumer(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle)
: BlobBytesConsumer(executionContext, blobDataHandle, nullptr)
{
}
BlobBytesConsumer::~BlobBytesConsumer()
{
}
BytesConsumer::Result BlobBytesConsumer::beginRead(const char** buffer, size_t* available)
{
*buffer = nullptr;
*available = 0;
if (m_state == PublicState::Closed) {
// It's possible that |cancel| has been called before the first
// |beginRead| call. That's why we need to check this condition
// before checking |isClean()|.
return Result::Done;
}
if (isClean()) {
KURL m_blobURL = BlobURL::createPublicURL(getExecutionContext()->getSecurityOrigin());
if (m_blobURL.isEmpty()) {
error();
} else {
BlobRegistry::registerPublicBlobURL(getExecutionContext()->getSecurityOrigin(), m_blobURL, m_blobDataHandle);
// m_loader is non-null only in tests.
if (!m_loader)
m_loader = createLoader();
ResourceRequest request(m_blobURL);
request.setRequestContext(WebURLRequest::RequestContextInternal);
request.setUseStreamOnResponse(true);
// We intentionally skip
// 'setExternalRequestStateFromRequestorAddressSpace', as 'blob:'
// can never be external.
m_loader->start(request);
}
m_blobDataHandle = nullptr;
}
DCHECK_NE(m_state, PublicState::Closed);
if (m_state == PublicState::Errored)
return Result::Error;
if (!m_body) {
// The response has not arrived.
return Result::ShouldWait;
}
auto result = m_body->beginRead(buffer, available);
switch (result) {
case Result::Ok:
case Result::ShouldWait:
break;
case Result::Done:
m_hasSeenEndOfData = true;
if (m_hasFinishedLoading)
close();
return m_state == PublicState::Closed ? Result::Done : Result::ShouldWait;
case Result::Error:
error();
break;
}
return result;
}
BytesConsumer::Result BlobBytesConsumer::endRead(size_t read)
{
DCHECK(m_body);
return m_body->endRead(read);
}
PassRefPtr<BlobDataHandle> BlobBytesConsumer::drainAsBlobDataHandle(BlobSizePolicy policy)
{
if (!isClean())
return nullptr;
DCHECK(m_blobDataHandle);
if (policy == BlobSizePolicy::DisallowBlobWithInvalidSize && m_blobDataHandle->size() == UINT64_MAX)
return nullptr;
close();
return m_blobDataHandle.release();
}
PassRefPtr<EncodedFormData> BlobBytesConsumer::drainAsFormData()
{
RefPtr<BlobDataHandle> handle = drainAsBlobDataHandle(BlobSizePolicy::AllowBlobWithInvalidSize);
if (!handle)
return nullptr;
RefPtr<EncodedFormData> formData = EncodedFormData::create();
formData->appendBlob(handle->uuid(), handle);
return formData.release();
}
void BlobBytesConsumer::setClient(BytesConsumer::Client* client)
{
DCHECK(!m_client);
DCHECK(client);
m_client = client;
}
void BlobBytesConsumer::clearClient()
{
m_client = nullptr;
}
void BlobBytesConsumer::cancel()
{
if (m_state == PublicState::Closed || m_state == PublicState::Errored)
return;
close();
if (m_body) {
m_body->cancel();
m_body = nullptr;
}
if (!m_blobURL.isEmpty()) {
BlobRegistry::revokePublicBlobURL(m_blobURL);
m_blobURL = KURL();
}
m_blobDataHandle = nullptr;
}
BytesConsumer::Error BlobBytesConsumer::getError() const
{
DCHECK_EQ(PublicState::Errored, m_state);
return Error("Failed to load a blob.");
}
BytesConsumer::PublicState BlobBytesConsumer::getPublicState() const
{
return m_state;
}
void BlobBytesConsumer::contextDestroyed()
{
if (m_state != PublicState::ReadableOrWaiting)
return;
BytesConsumer::Client* client = m_client;
error();
if (client)
client->onStateChange();
}
void BlobBytesConsumer::onStateChange()
{
if (m_state != PublicState::ReadableOrWaiting)
return;
DCHECK(m_body);
BytesConsumer::Client* client = m_client;
switch (m_body->getPublicState()) {
case PublicState::ReadableOrWaiting:
break;
case PublicState::Closed:
m_hasSeenEndOfData = true;
if (m_hasFinishedLoading)
close();
break;
case PublicState::Errored:
error();
break;
}
if (client)
client->onStateChange();
}
void BlobBytesConsumer::didReceiveResponse(unsigned long identifier, const ResourceResponse&, std::unique_ptr<WebDataConsumerHandle> handle)
{
DCHECK(handle);
DCHECK(!m_body);
DCHECK_EQ(PublicState::ReadableOrWaiting, m_state);
m_body = new BytesConsumerForDataConsumerHandle(getExecutionContext(), createFetchDataConsumerHandleFromWebHandle(std::move(handle)));
m_body->setClient(this);
if (isClean()) {
// This function is called synchronously in ThreadableLoader::start.
return;
}
onStateChange();
}
void BlobBytesConsumer::didFinishLoading(unsigned long identifier, double finishTime)
{
DCHECK_EQ(PublicState::ReadableOrWaiting, m_state);
m_hasFinishedLoading = true;
m_loader = nullptr;
if (!m_hasSeenEndOfData)
return;
DCHECK(!isClean());
BytesConsumer::Client* client = m_client;
close();
if (client)
client->onStateChange();
}
void BlobBytesConsumer::didFail(const ResourceError& e)
{
if (e.isCancellation()) {
DCHECK_EQ(PublicState::Closed, m_state);
return;
}
DCHECK_EQ(PublicState::ReadableOrWaiting, m_state);
m_loader = nullptr;
BytesConsumer::Client* client = m_client;
error();
if (isClean()) {
// This function is called synchronously in ThreadableLoader::start.
return;
}
if (client) {
client->onStateChange();
client = nullptr;
}
}
void BlobBytesConsumer::didFailRedirectCheck()
{
NOTREACHED();
}
DEFINE_TRACE(BlobBytesConsumer)
{
visitor->trace(m_body);
visitor->trace(m_client);
visitor->trace(m_loader);
BytesConsumer::trace(visitor);
BytesConsumer::Client::trace(visitor);
ContextLifecycleObserver::trace(visitor);
}
BlobBytesConsumer* BlobBytesConsumer::createForTesting(ExecutionContext* executionContext, PassRefPtr<BlobDataHandle> blobDataHandle, ThreadableLoader* loader)
{
return new BlobBytesConsumer(executionContext, blobDataHandle, loader);
}
ThreadableLoader* BlobBytesConsumer::createLoader()
{
ThreadableLoaderOptions options;
options.preflightPolicy = ConsiderPreflight;
options.crossOriginRequestPolicy = DenyCrossOriginRequests;
options.contentSecurityPolicyEnforcement = DoNotEnforceContentSecurityPolicy;
options.initiator = FetchInitiatorTypeNames::internal;
ResourceLoaderOptions resourceLoaderOptions;
resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
return ThreadableLoader::create(*getExecutionContext(), this, options, resourceLoaderOptions);
}
void BlobBytesConsumer::close()
{
DCHECK_EQ(m_state, PublicState::ReadableOrWaiting);
m_state = PublicState::Closed;
clear();
}
void BlobBytesConsumer::error()
{
DCHECK_EQ(m_state, PublicState::ReadableOrWaiting);
m_state = PublicState::Errored;
clear();
}
void BlobBytesConsumer::clear()
{
DCHECK_NE(m_state, PublicState::ReadableOrWaiting);
if (m_loader) {
m_loader->cancel();
m_loader = nullptr;
}
m_client = nullptr;
}
} // namespace blink
// Copyright 2016 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.
#ifndef BlobBytesConsumer_h
#define BlobBytesConsumer_h
#include "core/dom/ContextLifecycleObserver.h"
#include "core/loader/ThreadableLoaderClient.h"
#include "modules/ModulesExport.h"
#include "modules/fetch/BytesConsumer.h"
#include "platform/heap/Handle.h"
#include "wtf/PassRefPtr.h"
#include "wtf/RefPtr.h"
#include <memory>
namespace blink {
class BlobDataHandle;
class EncodedFormData;
class ExecutionContext;
class ThreadableLoader;
class WebDataConsumerHandle;
// A BlobBytesConsumer is created from a blob handle and it will
// return a valid handle from drainAsBlobDataHandle as much as possible.
class MODULES_EXPORT BlobBytesConsumer final : public BytesConsumer, public ContextLifecycleObserver, public BytesConsumer::Client, public ThreadableLoaderClient {
USING_GARBAGE_COLLECTED_MIXIN(BlobBytesConsumer);
USING_PRE_FINALIZER(BlobBytesConsumer, cancel);
public:
// |handle| can be null. In that case this consumer gets closed.
BlobBytesConsumer(ExecutionContext*, PassRefPtr<BlobDataHandle> /* handle */);
~BlobBytesConsumer() override;
// BytesConsumer implementation
Result beginRead(const char** buffer, size_t* available) override;
Result endRead(size_t readSize) override;
PassRefPtr<BlobDataHandle> drainAsBlobDataHandle(BlobSizePolicy) override;
PassRefPtr<EncodedFormData> drainAsFormData() override;
void setClient(BytesConsumer::Client*) override;
void clearClient() override;
void cancel() override;
PublicState getPublicState() const override;
Error getError() const override;
String debugName() const override { return "BlobBytesConsumer"; }
// ContextLifecycleObserver implementation
void contextDestroyed() override;
// BytesConsumer::Client implementation
void onStateChange() override;
// ThreadableLoaderClient implementation
void didReceiveResponse(unsigned long identifier, const ResourceResponse&, std::unique_ptr<WebDataConsumerHandle>) override;
void didFinishLoading(unsigned long identifier, double finishTime) override;
void didFail(const ResourceError&) override;
void didFailRedirectCheck() override;
DECLARE_TRACE();
static BlobBytesConsumer* createForTesting(ExecutionContext*, PassRefPtr<BlobDataHandle>, ThreadableLoader*);
private:
BlobBytesConsumer(ExecutionContext*, PassRefPtr<BlobDataHandle>, ThreadableLoader*);
ThreadableLoader* createLoader();
void didFailInternal();
bool isClean() const { return m_blobDataHandle.get(); }
void close();
void error();
void clear();
KURL m_blobURL;
RefPtr<BlobDataHandle> m_blobDataHandle;
Member<BytesConsumer> m_body;
Member<BytesConsumer::Client> m_client;
Member<ThreadableLoader> m_loader;
PublicState m_state = PublicState::ReadableOrWaiting;
// These two booleans are meaningful only when m_state is ReadableOrWaiting.
bool m_hasSeenEndOfData = false;
bool m_hasFinishedLoading = false;
};
} // namespace blink
#endif // BlobBytesConsumer_h
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include "core/dom/ExecutionContext.h" #include "core/dom/ExecutionContext.h"
#include "core/dom/TaskRunnerHelper.h" #include "core/dom/TaskRunnerHelper.h"
#include "modules/fetch/BytesConsumerForDataConsumerHandle.h" #include "modules/fetch/BlobBytesConsumer.h"
#include "modules/fetch/FetchBlobDataConsumerHandle.h" #include "modules/fetch/FetchBlobDataConsumerHandle.h"
#include "platform/blob/BlobData.h" #include "platform/blob/BlobData.h"
#include "public/platform/WebTaskRunner.h" #include "public/platform/WebTaskRunner.h"
...@@ -324,9 +324,8 @@ void BytesConsumer::tee(ExecutionContext* executionContext, BytesConsumer* src, ...@@ -324,9 +324,8 @@ void BytesConsumer::tee(ExecutionContext* executionContext, BytesConsumer* src,
if (blobDataHandle) { if (blobDataHandle) {
// Register a client in order to be consistent. // Register a client in order to be consistent.
src->setClient(new NoopClient); src->setClient(new NoopClient);
// TODO(yhirano): Do not use FetchBlobDataConsumerHandle. *dest1 = new BlobBytesConsumer(executionContext, blobDataHandle);
*dest1 = new BytesConsumerForDataConsumerHandle(executionContext, FetchBlobDataConsumerHandle::create(executionContext, blobDataHandle)); *dest2 = new BlobBytesConsumer(executionContext, blobDataHandle);
*dest2 = new BytesConsumerForDataConsumerHandle(executionContext, FetchBlobDataConsumerHandle::create(executionContext, blobDataHandle));
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment