Commit 2e3335cb authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

Rewrite BlobBytesConsumer to not rely on blob URLs.

The existing implementation depended on the non-mojo blob URL code path,
and so would break soon after mojo blob URLs ship when the old code gets
deleted. So rewrite the implementation to read blobs more directly rather
than going through a blob URL.

Bug: 859109
Change-Id: Ia6f2432e48ac5304ea8f5598d293e31c2c1e1905
Reviewed-on: https://chromium-review.googlesource.com/1120679
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#572363}
parent ad08d8f0
...@@ -30,7 +30,9 @@ promise_test(function(test) { ...@@ -30,7 +30,9 @@ promise_test(function(test) {
var closedPromise = reader.closed.then(function() { var closedPromise = reader.closed.then(function() {
return reader.cancel(); return reader.cancel();
}); });
reader.read(); reader.read().then(function readMore({done, value}) {
if (!done) return reader.read().then(readMore);
});
return closedPromise; return closedPromise;
}, "Cancelling a closed blob Response stream"); }, "Cancelling a closed blob Response stream");
......
...@@ -4,132 +4,52 @@ ...@@ -4,132 +4,52 @@
#include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h" #include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
#include "third_party/blink/renderer/core/fetch/bytes_consumer_for_data_consumer_handle.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/loader/threadable_loader.h" #include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/blob/blob_registry.h"
#include "third_party/blink/renderer/platform/blob/blob_url.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
namespace blink { namespace blink {
BlobBytesConsumer::BlobBytesConsumer(
ExecutionContext* execution_context,
scoped_refptr<BlobDataHandle> blob_data_handle,
ThreadableLoader* loader)
: ContextLifecycleObserver(execution_context),
blob_data_handle_(std::move(blob_data_handle)),
loader_(loader) {
if (!blob_data_handle_) {
// Note that |m_loader| is non-null only in tests.
if (loader_) {
loader_->Cancel();
loader_ = nullptr;
}
state_ = PublicState::kClosed;
}
}
BlobBytesConsumer::BlobBytesConsumer( BlobBytesConsumer::BlobBytesConsumer(
ExecutionContext* execution_context, ExecutionContext* execution_context,
scoped_refptr<BlobDataHandle> blob_data_handle) scoped_refptr<BlobDataHandle> blob_data_handle)
: BlobBytesConsumer(execution_context, : execution_context_(execution_context),
std::move(blob_data_handle), blob_data_handle_(std::move(blob_data_handle)) {}
nullptr) {}
BlobBytesConsumer::~BlobBytesConsumer() { BlobBytesConsumer::~BlobBytesConsumer() {
if (!blob_url_.IsEmpty())
BlobRegistry::RevokePublicBlobURL(blob_url_);
} }
BytesConsumer::Result BlobBytesConsumer::BeginRead(const char** buffer, BytesConsumer::Result BlobBytesConsumer::BeginRead(const char** buffer,
size_t* available) { size_t* available) {
*buffer = nullptr; if (!nested_consumer_) {
*available = 0; if (!blob_data_handle_)
if (state_ == PublicState::kClosed) {
// 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::kDone; return Result::kDone;
}
if (IsClean()) {
DCHECK(blob_url_.IsEmpty());
blob_url_ =
BlobURL::CreatePublicURL(GetExecutionContext()->GetSecurityOrigin());
if (blob_url_.IsEmpty()) {
GetError();
} else {
BlobRegistry::RegisterPublicBlobURL(
GetExecutionContext()->GetMutableSecurityOrigin(), blob_url_,
blob_data_handle_);
// m_loader is non-null only in tests. scoped_refptr<EncodedFormData> form_data = EncodedFormData::Create();
if (!loader_) form_data->AppendDataPipe(base::MakeRefCounted<WrappedDataPipeGetter>(
loader_ = CreateLoader(); blob_data_handle_->AsDataPipeGetter()));
nested_consumer_ =
ResourceRequest request(blob_url_); new FormDataBytesConsumer(execution_context_, std::move(form_data));
request.SetRequestContext(WebURLRequest::kRequestContextInternal); if (client_)
request.SetFetchRequestMode( nested_consumer_->SetClient(client_);
network::mojom::FetchRequestMode::kSameOrigin);
request.SetFetchCredentialsMode(
network::mojom::FetchCredentialsMode::kOmit);
request.SetUseStreamOnResponse(true);
// We intentionally skip
// 'setExternalRequestStateFromRequestorAddressSpace', as 'blob:'
// can never be external.
loader_->Start(request);
}
blob_data_handle_ = nullptr; blob_data_handle_ = nullptr;
client_ = nullptr;
} }
DCHECK_NE(state_, PublicState::kClosed); return nested_consumer_->BeginRead(buffer, available);
if (state_ == PublicState::kErrored)
return Result::kError;
if (!body_) {
// The response has not arrived.
return Result::kShouldWait;
}
auto result = body_->BeginRead(buffer, available);
switch (result) {
case Result::kOk:
case Result::kShouldWait:
break;
case Result::kDone:
has_seen_end_of_data_ = true;
if (has_finished_loading_)
Close();
return state_ == PublicState::kClosed ? Result::kDone
: Result::kShouldWait;
case Result::kError:
GetError();
break;
}
return result;
} }
BytesConsumer::Result BlobBytesConsumer::EndRead(size_t read) { BytesConsumer::Result BlobBytesConsumer::EndRead(size_t read) {
DCHECK(body_); DCHECK(nested_consumer_);
return body_->EndRead(read); return nested_consumer_->EndRead(read);
} }
scoped_refptr<BlobDataHandle> BlobBytesConsumer::DrainAsBlobDataHandle( scoped_refptr<BlobDataHandle> BlobBytesConsumer::DrainAsBlobDataHandle(
BlobSizePolicy policy) { BlobSizePolicy policy) {
if (!IsClean()) if (!blob_data_handle_)
return nullptr; return nullptr;
DCHECK(blob_data_handle_);
if (policy == BlobSizePolicy::kDisallowBlobWithInvalidSize && if (policy == BlobSizePolicy::kDisallowBlobWithInvalidSize &&
blob_data_handle_->size() == UINT64_MAX) blob_data_handle_->size() == UINT64_MAX)
return nullptr; return nullptr;
Close();
return std::move(blob_data_handle_); return std::move(blob_data_handle_);
} }
...@@ -146,172 +66,43 @@ scoped_refptr<EncodedFormData> BlobBytesConsumer::DrainAsFormData() { ...@@ -146,172 +66,43 @@ scoped_refptr<EncodedFormData> BlobBytesConsumer::DrainAsFormData() {
void BlobBytesConsumer::SetClient(BytesConsumer::Client* client) { void BlobBytesConsumer::SetClient(BytesConsumer::Client* client) {
DCHECK(!client_); DCHECK(!client_);
DCHECK(client); DCHECK(client);
if (nested_consumer_)
nested_consumer_->SetClient(client);
else
client_ = client; client_ = client;
} }
void BlobBytesConsumer::ClearClient() { void BlobBytesConsumer::ClearClient() {
client_ = nullptr; client_ = nullptr;
if (nested_consumer_)
nested_consumer_->ClearClient();
} }
void BlobBytesConsumer::Cancel() { void BlobBytesConsumer::Cancel() {
if (state_ == PublicState::kClosed || state_ == PublicState::kErrored) if (nested_consumer_)
return; nested_consumer_->Cancel();
Close();
if (body_) {
body_->Cancel();
body_ = nullptr;
}
if (!blob_url_.IsEmpty()) {
BlobRegistry::RevokePublicBlobURL(blob_url_);
blob_url_ = KURL();
}
blob_data_handle_ = nullptr; blob_data_handle_ = nullptr;
client_ = nullptr;
} }
BytesConsumer::Error BlobBytesConsumer::GetError() const { BytesConsumer::Error BlobBytesConsumer::GetError() const {
DCHECK_EQ(PublicState::kErrored, state_); DCHECK(nested_consumer_);
return Error("Failed to load a blob."); return nested_consumer_->GetError();
} }
BytesConsumer::PublicState BlobBytesConsumer::GetPublicState() const { BytesConsumer::PublicState BlobBytesConsumer::GetPublicState() const {
return state_; if (!nested_consumer_) {
} return blob_data_handle_ ? PublicState::kReadableOrWaiting
: PublicState::kClosed;
void BlobBytesConsumer::ContextDestroyed(ExecutionContext*) {
if (state_ != PublicState::kReadableOrWaiting)
return;
BytesConsumer::Client* client = client_;
GetError();
if (client)
client->OnStateChange();
}
void BlobBytesConsumer::OnStateChange() {
if (state_ != PublicState::kReadableOrWaiting)
return;
DCHECK(body_);
BytesConsumer::Client* client = client_;
switch (body_->GetPublicState()) {
case PublicState::kReadableOrWaiting:
break;
case PublicState::kClosed:
has_seen_end_of_data_ = true;
if (has_finished_loading_)
Close();
break;
case PublicState::kErrored:
GetError();
break;
}
if (client)
client->OnStateChange();
}
void BlobBytesConsumer::DidReceiveResponse(
unsigned long identifier,
const ResourceResponse&,
std::unique_ptr<WebDataConsumerHandle> handle) {
DCHECK(handle);
DCHECK(!body_);
DCHECK_EQ(PublicState::kReadableOrWaiting, state_);
body_ = new BytesConsumerForDataConsumerHandle(GetExecutionContext(),
std::move(handle));
body_->SetClient(this);
if (IsClean()) {
// This function is called synchronously in ThreadableLoader::start.
return;
}
OnStateChange();
}
void BlobBytesConsumer::DidFinishLoading(unsigned long identifier) {
DCHECK_EQ(PublicState::kReadableOrWaiting, state_);
has_finished_loading_ = true;
loader_ = nullptr;
if (!has_seen_end_of_data_)
return;
DCHECK(!IsClean());
BytesConsumer::Client* client = client_;
Close();
if (client)
client->OnStateChange();
}
void BlobBytesConsumer::DidFail(const ResourceError& e) {
if (e.IsCancellation()) {
if (state_ != PublicState::kReadableOrWaiting)
return;
}
DCHECK_EQ(PublicState::kReadableOrWaiting, state_);
loader_ = nullptr;
BytesConsumer::Client* client = client_;
GetError();
if (IsClean()) {
// This function is called synchronously in ThreadableLoader::start.
return;
}
if (client) {
client->OnStateChange();
client = nullptr;
} }
} return nested_consumer_->GetPublicState();
void BlobBytesConsumer::DidFailRedirectCheck() {
NOTREACHED();
} }
void BlobBytesConsumer::Trace(blink::Visitor* visitor) { void BlobBytesConsumer::Trace(blink::Visitor* visitor) {
visitor->Trace(body_); visitor->Trace(execution_context_);
visitor->Trace(nested_consumer_);
visitor->Trace(client_); visitor->Trace(client_);
visitor->Trace(loader_);
BytesConsumer::Trace(visitor); BytesConsumer::Trace(visitor);
BytesConsumer::Client::Trace(visitor);
ContextLifecycleObserver::Trace(visitor);
}
BlobBytesConsumer* BlobBytesConsumer::CreateForTesting(
ExecutionContext* execution_context,
scoped_refptr<BlobDataHandle> blob_data_handle,
ThreadableLoader* loader) {
return new BlobBytesConsumer(execution_context, std::move(blob_data_handle),
loader);
}
ThreadableLoader* BlobBytesConsumer::CreateLoader() {
ThreadableLoaderOptions options;
ResourceLoaderOptions resource_loader_options;
resource_loader_options.data_buffering_policy = kDoNotBufferData;
resource_loader_options.initiator_info.name =
FetchInitiatorTypeNames::internal;
return ThreadableLoader::Create(*GetExecutionContext(), this, options,
resource_loader_options);
}
void BlobBytesConsumer::Close() {
DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
state_ = PublicState::kClosed;
Clear();
}
void BlobBytesConsumer::GetError() {
DCHECK_EQ(state_, PublicState::kReadableOrWaiting);
state_ = PublicState::kErrored;
Clear();
}
void BlobBytesConsumer::Clear() {
DCHECK_NE(state_, PublicState::kReadableOrWaiting);
if (loader_) {
loader_->Cancel();
loader_ = nullptr;
}
client_ = nullptr;
} }
} // namespace blink } // namespace blink
...@@ -8,28 +8,17 @@ ...@@ -8,28 +8,17 @@
#include <memory> #include <memory>
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/fetch/bytes_consumer.h" #include "third_party/blink/renderer/core/fetch/bytes_consumer.h"
#include "third_party/blink/renderer/core/loader/threadable_loader_client.h"
#include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/heap/handle.h"
namespace blink { namespace blink {
class BlobDataHandle; class BlobDataHandle;
class EncodedFormData;
class ExecutionContext; class ExecutionContext;
class ThreadableLoader;
class WebDataConsumerHandle;
// A BlobBytesConsumer is created from a blob handle and it will // A BlobBytesConsumer is created from a blob handle and it will
// return a valid handle from drainAsBlobDataHandle as much as possible. // return a valid handle from drainAsBlobDataHandle as much as possible.
class CORE_EXPORT BlobBytesConsumer final : public BytesConsumer, class CORE_EXPORT BlobBytesConsumer final : public BytesConsumer {
public ContextLifecycleObserver,
public BytesConsumer::Client,
public ThreadableLoaderClient {
USING_GARBAGE_COLLECTED_MIXIN(BlobBytesConsumer);
USING_PRE_FINALIZER(BlobBytesConsumer, Cancel);
public: public:
// |handle| can be null. In that case this consumer gets closed. // |handle| can be null. In that case this consumer gets closed.
BlobBytesConsumer(ExecutionContext*, BlobBytesConsumer(ExecutionContext*,
...@@ -48,47 +37,13 @@ class CORE_EXPORT BlobBytesConsumer final : public BytesConsumer, ...@@ -48,47 +37,13 @@ class CORE_EXPORT BlobBytesConsumer final : public BytesConsumer,
Error GetError() const override; Error GetError() const override;
String DebugName() const override { return "BlobBytesConsumer"; } String DebugName() const override { return "BlobBytesConsumer"; }
// ContextLifecycleObserver implementation
void ContextDestroyed(ExecutionContext*) 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) override;
void DidFail(const ResourceError&) override;
void DidFailRedirectCheck() override;
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
static BlobBytesConsumer* CreateForTesting(ExecutionContext*,
scoped_refptr<BlobDataHandle>,
ThreadableLoader*);
private: private:
BlobBytesConsumer(ExecutionContext*, Member<ExecutionContext> execution_context_;
scoped_refptr<BlobDataHandle>,
ThreadableLoader*);
ThreadableLoader* CreateLoader();
void DidFailInternal();
bool IsClean() const { return blob_data_handle_.get(); }
void Close();
void GetError();
void Clear();
KURL blob_url_;
scoped_refptr<BlobDataHandle> blob_data_handle_; scoped_refptr<BlobDataHandle> blob_data_handle_;
Member<BytesConsumer> body_; Member<BytesConsumer> nested_consumer_;
Member<BytesConsumer::Client> client_; Member<BytesConsumer::Client> client_;
Member<ThreadableLoader> loader_;
PublicState state_ = PublicState::kReadableOrWaiting;
// These two booleans are meaningful only when m_state is ReadableOrWaiting.
bool has_seen_end_of_data_ = false;
bool has_finished_loading_ = false;
}; };
} // namespace blink } // namespace blink
......
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
#include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h" #include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h" #include "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h"
#include "third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.h" #include "third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.h"
#include "third_party/blink/renderer/core/loader/threadable_loader.h" #include "third_party/blink/renderer/core/loader/threadable_loader.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/blob/blob_data.h" #include "third_party/blink/renderer/platform/blob/blob_data.h"
#include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
#include "third_party/blink/renderer/platform/network/encoded_form_data.h" #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
...@@ -25,92 +27,6 @@ using PublicState = BytesConsumer::PublicState; ...@@ -25,92 +27,6 @@ using PublicState = BytesConsumer::PublicState;
using ReplayingHandle = DataConsumerHandleTestUtil::ReplayingHandle; using ReplayingHandle = DataConsumerHandleTestUtil::ReplayingHandle;
using Result = BytesConsumer::Result; using Result = BytesConsumer::Result;
class TestThreadableLoader : public ThreadableLoader {
public:
~TestThreadableLoader() override {
EXPECT_FALSE(should_be_cancelled_ && !is_cancelled_)
<< "The loader should be cancelled but is not cancelled.";
}
void Start(const ResourceRequest& request) override { is_started_ = true; }
void OverrideTimeout(unsigned long timeout_milliseconds) override {
ADD_FAILURE() << "overrideTimeout should not be called.";
}
void Cancel() override { is_cancelled_ = true; }
void Detach() override { NOTREACHED(); }
bool IsStarted() const { return is_started_; }
bool IsCancelled() const { return is_cancelled_; }
void SetShouldBeCancelled() { should_be_cancelled_ = true; }
private:
bool is_started_ = false;
bool is_cancelled_ = false;
bool should_be_cancelled_ = false;
};
class SyncLoadingTestThreadableLoader : public ThreadableLoader {
public:
~SyncLoadingTestThreadableLoader() override { DCHECK(!handle_); }
void Start(const ResourceRequest& request) override {
is_started_ = true;
client_->DidReceiveResponse(0, ResourceResponse(), std::move(handle_));
client_->DidFinishLoading(0);
}
void OverrideTimeout(unsigned long timeout_milliseconds) override {
ADD_FAILURE() << "overrideTimeout should not be called.";
}
void Cancel() override { is_cancelled_ = true; }
void Detach() override { NOTREACHED(); }
bool IsStarted() const { return is_started_; }
bool IsCancelled() const { return is_cancelled_; }
void SetClient(ThreadableLoaderClient* client) { client_ = client; }
void SetHandle(std::unique_ptr<WebDataConsumerHandle> handle) {
handle_ = std::move(handle);
}
private:
bool is_started_ = false;
bool is_cancelled_ = false;
ThreadableLoaderClient* client_ = nullptr;
std::unique_ptr<WebDataConsumerHandle> handle_;
};
class SyncErrorTestThreadableLoader : public ThreadableLoader {
public:
~SyncErrorTestThreadableLoader() override {}
void Start(const ResourceRequest& request) override {
is_started_ = true;
client_->DidFail(ResourceError::Failure(NullURL()));
}
void OverrideTimeout(unsigned long timeout_milliseconds) override {
ADD_FAILURE() << "overrideTimeout should not be called.";
}
void Cancel() override { is_cancelled_ = true; }
void Detach() override { NOTREACHED(); }
bool IsStarted() const { return is_started_; }
bool IsCancelled() const { return is_cancelled_; }
void SetClient(ThreadableLoaderClient* client) { client_ = client; }
private:
bool is_started_ = false;
bool is_cancelled_ = false;
ThreadableLoaderClient* client_ = nullptr;
};
class BlobBytesConsumerTestClient final class BlobBytesConsumerTestClient final
: public GarbageCollectedFinalized<BlobBytesConsumerTestClient>, : public GarbageCollectedFinalized<BlobBytesConsumerTestClient>,
public BytesConsumer::Client { public BytesConsumer::Client {
...@@ -128,144 +44,71 @@ class BlobBytesConsumerTestClient final ...@@ -128,144 +44,71 @@ class BlobBytesConsumerTestClient final
class BlobBytesConsumerTest : public PageTestBase { class BlobBytesConsumerTest : public PageTestBase {
public: public:
void SetUp() override { PageTestBase::SetUp(IntSize(1, 1)); } void SetUp() override { PageTestBase::SetUp(IntSize(1, 1)); }
scoped_refptr<BlobDataHandle> CreateBlob(const String& body) {
mojom::blink::BlobPtrInfo mojo_blob;
mojo::MakeStrongBinding(
std::make_unique<FakeBlob>(kBlobUUID, body, &blob_state_),
MakeRequest(&mojo_blob));
return BlobDataHandle::Create(kBlobUUID, "", body.length(),
std::move(mojo_blob));
}
bool DidStartLoading() {
base::RunLoop().RunUntilIdle();
return blob_state_.did_initiate_read_operation;
}
private:
const String kBlobUUID = "blob-id";
FakeBlob::State blob_state_;
}; };
TEST_F(BlobBytesConsumerTest, TwoPhaseRead) { TEST_F(BlobBytesConsumerTest, TwoPhaseRead) {
scoped_refptr<BlobDataHandle> blob_data_handle = String body = "hello, world";
BlobDataHandle::Create(BlobData::Create(), 12345); scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob(body);
TestThreadableLoader* loader = new TestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting( BlobBytesConsumer* consumer =
&GetDocument(), blob_data_handle, loader); new BlobBytesConsumer(&GetDocument(), blob_data_handle);
std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello, "));
src->Add(DataConsumerCommand(DataConsumerCommand::kWait));
src->Add(DataConsumerCommand(DataConsumerCommand::kData, "world"));
src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
const char* buffer = nullptr; const char* buffer = nullptr;
size_t available = 0; size_t available = 0;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available)); EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_TRUE(loader->IsStarted()); EXPECT_TRUE(DidStartLoading());
EXPECT_FALSE(consumer->DrainAsBlobDataHandle( EXPECT_FALSE(consumer->DrainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize)); BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize));
EXPECT_FALSE(consumer->DrainAsFormData()); EXPECT_FALSE(consumer->DrainAsFormData());
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
consumer->DidReceiveResponse(0, ResourceResponse(), std::move(src));
consumer->DidFinishLoading(0);
auto result = (new BytesConsumerTestUtil::TwoPhaseReader(consumer))->Run(); auto result = (new BytesConsumerTestUtil::TwoPhaseReader(consumer))->Run();
EXPECT_EQ(Result::kDone, result.first); EXPECT_EQ(Result::kDone, result.first);
EXPECT_EQ("hello, world", EXPECT_EQ("hello, world",
BytesConsumerTestUtil::CharVectorToString(result.second)); BytesConsumerTestUtil::CharVectorToString(result.second));
} }
TEST_F(BlobBytesConsumerTest, FailLoading) {
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
TestThreadableLoader* loader = new TestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client);
const char* buffer = nullptr;
size_t available = 0;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_TRUE(loader->IsStarted());
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
int num_on_state_change_called = client->NumOnStateChangeCalled();
consumer->DidFail(ResourceError::Failure(NullURL()));
EXPECT_EQ(num_on_state_change_called + 1, client->NumOnStateChangeCalled());
EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
}
TEST_F(BlobBytesConsumerTest, FailLoadingAfterResponseReceived) {
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
TestThreadableLoader* loader = new TestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client);
const char* buffer = nullptr;
size_t available;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_TRUE(loader->IsStarted());
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
int num_on_state_change_called = client->NumOnStateChangeCalled();
consumer->DidReceiveResponse(
0, ResourceResponse(),
DataConsumerHandleTestUtil::CreateWaitingDataConsumerHandle());
EXPECT_EQ(num_on_state_change_called + 1, client->NumOnStateChangeCalled());
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
consumer->DidFail(ResourceError::Failure(NullURL()));
EXPECT_EQ(num_on_state_change_called + 2, client->NumOnStateChangeCalled());
EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
}
TEST_F(BlobBytesConsumerTest, FailAccessControlCheck) {
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
TestThreadableLoader* loader = new TestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client);
const char* buffer = nullptr;
size_t available;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_TRUE(loader->IsStarted());
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
int num_on_state_change_called = client->NumOnStateChangeCalled();
consumer->DidFail(ResourceError::Failure(NullURL()));
EXPECT_EQ(num_on_state_change_called + 1, client->NumOnStateChangeCalled());
EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState());
EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
}
TEST_F(BlobBytesConsumerTest, CancelBeforeStarting) { TEST_F(BlobBytesConsumerTest, CancelBeforeStarting) {
scoped_refptr<BlobDataHandle> blob_data_handle = scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob("foo bar");
BlobDataHandle::Create(BlobData::Create(), 12345); BlobBytesConsumer* consumer =
TestThreadableLoader* loader = new TestThreadableLoader(); new BlobBytesConsumer(&GetDocument(), blob_data_handle);
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient(); BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client); consumer->SetClient(client);
consumer->Cancel(); consumer->Cancel();
// This should be FALSE in production, but TRUE here because we set the
// loader before starting loading in tests.
EXPECT_TRUE(loader->IsCancelled());
const char* buffer = nullptr; const char* buffer = nullptr;
size_t available; size_t available;
EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available)); EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState()); EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
EXPECT_EQ(0, client->NumOnStateChangeCalled()); EXPECT_EQ(0, client->NumOnStateChangeCalled());
} }
TEST_F(BlobBytesConsumerTest, CancelAfterStarting) { TEST_F(BlobBytesConsumerTest, CancelAfterStarting) {
scoped_refptr<BlobDataHandle> blob_data_handle = scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob("foo bar");
BlobDataHandle::Create(BlobData::Create(), 12345); BlobBytesConsumer* consumer =
TestThreadableLoader* loader = new TestThreadableLoader(); new BlobBytesConsumer(&GetDocument(), blob_data_handle);
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient(); BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client); consumer->SetClient(client);
...@@ -273,130 +116,43 @@ TEST_F(BlobBytesConsumerTest, CancelAfterStarting) { ...@@ -273,130 +116,43 @@ TEST_F(BlobBytesConsumerTest, CancelAfterStarting) {
size_t available; size_t available;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available)); EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_TRUE(loader->IsStarted());
EXPECT_EQ(0, client->NumOnStateChangeCalled()); EXPECT_EQ(0, client->NumOnStateChangeCalled());
consumer->Cancel(); consumer->Cancel();
EXPECT_TRUE(loader->IsCancelled());
EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState()); EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available)); EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(0, client->NumOnStateChangeCalled()); EXPECT_EQ(0, client->NumOnStateChangeCalled());
} EXPECT_TRUE(DidStartLoading());
TEST_F(BlobBytesConsumerTest, ReadLastChunkBeforeDidFinishLoadingArrives) {
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
TestThreadableLoader* loader = new TestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client);
std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello"));
src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted());
const char* buffer = nullptr;
size_t available;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_TRUE(loader->IsStarted());
EXPECT_EQ(0, client->NumOnStateChangeCalled());
consumer->DidReceiveResponse(0, ResourceResponse(), std::move(src));
EXPECT_EQ(1, client->NumOnStateChangeCalled());
test::RunPendingTasks();
EXPECT_EQ(2, client->NumOnStateChangeCalled());
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available));
ASSERT_EQ(5u, available);
EXPECT_EQ("hello", String(buffer, available));
ASSERT_EQ(Result::kOk, consumer->EndRead(available));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
ASSERT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
consumer->DidFinishLoading(0);
EXPECT_EQ(3, client->NumOnStateChangeCalled());
EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
ASSERT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
}
TEST_F(BlobBytesConsumerTest, ReadLastChunkAfterDidFinishLoadingArrives) {
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
TestThreadableLoader* loader = new TestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client);
std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello"));
src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted());
const char* buffer = nullptr;
size_t available;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_TRUE(loader->IsStarted());
EXPECT_EQ(0, client->NumOnStateChangeCalled());
consumer->DidReceiveResponse(0, ResourceResponse(), std::move(src));
EXPECT_EQ(1, client->NumOnStateChangeCalled());
test::RunPendingTasks();
EXPECT_EQ(2, client->NumOnStateChangeCalled());
consumer->DidFinishLoading(0);
test::RunPendingTasks();
EXPECT_EQ(2, client->NumOnStateChangeCalled());
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available));
ASSERT_EQ(5u, available);
EXPECT_EQ("hello", String(buffer, available));
ASSERT_EQ(Result::kOk, consumer->EndRead(available));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_EQ(Result::kDone, consumer->BeginRead(&buffer, &available));
EXPECT_EQ(2, client->NumOnStateChangeCalled());
} }
TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle) { TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle) {
scoped_refptr<BlobDataHandle> blob_data_handle = String body = "hello, world";
BlobDataHandle::Create(BlobData::Create(), 12345); scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob(body);
TestThreadableLoader* loader = new TestThreadableLoader(); BlobBytesConsumer* consumer =
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting( new BlobBytesConsumer(&GetDocument(), blob_data_handle);
&GetDocument(), blob_data_handle, loader);
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
scoped_refptr<BlobDataHandle> result = consumer->DrainAsBlobDataHandle( scoped_refptr<BlobDataHandle> result = consumer->DrainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize); BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize);
ASSERT_TRUE(result); ASSERT_TRUE(result);
EXPECT_FALSE(consumer->DrainAsBlobDataHandle( EXPECT_FALSE(consumer->DrainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize)); BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize));
EXPECT_EQ(12345u, result->size()); EXPECT_EQ(body.length(), result->size());
EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState()); EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
} }
TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_2) { TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_2) {
scoped_refptr<BlobDataHandle> blob_data_handle = scoped_refptr<BlobDataHandle> blob_data_handle = BlobDataHandle::Create(
BlobDataHandle::Create(BlobData::Create(), -1); "uuid", "", -1, CreateBlob("foo bar")->CloneBlobPtr().PassInterface());
TestThreadableLoader* loader = new TestThreadableLoader(); ;
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting( BlobBytesConsumer* consumer =
&GetDocument(), blob_data_handle, loader); new BlobBytesConsumer(&GetDocument(), blob_data_handle);
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
scoped_refptr<BlobDataHandle> result = consumer->DrainAsBlobDataHandle( scoped_refptr<BlobDataHandle> result = consumer->DrainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize); BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize);
...@@ -405,109 +161,44 @@ TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_2) { ...@@ -405,109 +161,44 @@ TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_2) {
BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize)); BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize));
EXPECT_EQ(UINT64_MAX, result->size()); EXPECT_EQ(UINT64_MAX, result->size());
EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState()); EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
} }
TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_3) { TEST_F(BlobBytesConsumerTest, DrainAsBlobDataHandle_3) {
scoped_refptr<BlobDataHandle> blob_data_handle = scoped_refptr<BlobDataHandle> blob_data_handle = BlobDataHandle::Create(
BlobDataHandle::Create(BlobData::Create(), -1); "uuid", "", -1, CreateBlob("foo bar")->CloneBlobPtr().PassInterface());
TestThreadableLoader* loader = new TestThreadableLoader(); ;
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting( BlobBytesConsumer* consumer =
&GetDocument(), blob_data_handle, loader); new BlobBytesConsumer(&GetDocument(), blob_data_handle);
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
EXPECT_FALSE(consumer->DrainAsBlobDataHandle( EXPECT_FALSE(consumer->DrainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize)); BytesConsumer::BlobSizePolicy::kDisallowBlobWithInvalidSize));
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
} }
TEST_F(BlobBytesConsumerTest, DrainAsFormData) { TEST_F(BlobBytesConsumerTest, DrainAsFormData) {
scoped_refptr<BlobDataHandle> blob_data_handle = String body = "hello, world";
BlobDataHandle::Create(BlobData::Create(), 12345); scoped_refptr<BlobDataHandle> blob_data_handle = CreateBlob(body);
TestThreadableLoader* loader = new TestThreadableLoader(); BlobBytesConsumer* consumer =
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting( new BlobBytesConsumer(&GetDocument(), blob_data_handle);
&GetDocument(), blob_data_handle, loader);
EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
scoped_refptr<EncodedFormData> result = consumer->DrainAsFormData(); scoped_refptr<EncodedFormData> result = consumer->DrainAsFormData();
ASSERT_TRUE(result); ASSERT_TRUE(result);
ASSERT_EQ(1u, result->Elements().size()); ASSERT_EQ(1u, result->Elements().size());
ASSERT_EQ(FormDataElement::kEncodedBlob, result->Elements()[0].type_); ASSERT_EQ(FormDataElement::kEncodedBlob, result->Elements()[0].type_);
ASSERT_TRUE(result->Elements()[0].optional_blob_data_handle_); ASSERT_TRUE(result->Elements()[0].optional_blob_data_handle_);
EXPECT_EQ(12345u, result->Elements()[0].optional_blob_data_handle_->size()); EXPECT_EQ(body.length(),
result->Elements()[0].optional_blob_data_handle_->size());
EXPECT_EQ(blob_data_handle->Uuid(), result->Elements()[0].blob_uuid_); EXPECT_EQ(blob_data_handle->Uuid(), result->Elements()[0].blob_uuid_);
EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState()); EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState());
EXPECT_FALSE(loader->IsStarted()); EXPECT_FALSE(DidStartLoading());
}
TEST_F(BlobBytesConsumerTest, LoaderShouldBeCancelled) {
{
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
TestThreadableLoader* loader = new TestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
const char* buffer = nullptr;
size_t available;
EXPECT_EQ(Result::kShouldWait, consumer->BeginRead(&buffer, &available));
EXPECT_TRUE(loader->IsStarted());
loader->SetShouldBeCancelled();
}
ThreadState::Current()->CollectAllGarbage();
}
TEST_F(BlobBytesConsumerTest, SyncErrorDispatch) {
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
SyncErrorTestThreadableLoader* loader = new SyncErrorTestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
loader->SetClient(consumer);
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client);
const char* buffer = nullptr;
size_t available;
EXPECT_EQ(Result::kError, consumer->BeginRead(&buffer, &available));
EXPECT_TRUE(loader->IsStarted());
EXPECT_EQ(0, client->NumOnStateChangeCalled());
EXPECT_EQ(BytesConsumer::PublicState::kErrored, consumer->GetPublicState());
}
TEST_F(BlobBytesConsumerTest, SyncLoading) {
scoped_refptr<BlobDataHandle> blob_data_handle =
BlobDataHandle::Create(BlobData::Create(), 12345);
SyncLoadingTestThreadableLoader* loader =
new SyncLoadingTestThreadableLoader();
BlobBytesConsumer* consumer = BlobBytesConsumer::CreateForTesting(
&GetDocument(), blob_data_handle, loader);
std::unique_ptr<ReplayingHandle> src = ReplayingHandle::Create();
src->Add(DataConsumerCommand(DataConsumerCommand::kData, "hello, "));
src->Add(DataConsumerCommand(DataConsumerCommand::kWait));
src->Add(DataConsumerCommand(DataConsumerCommand::kData, "world"));
src->Add(DataConsumerCommand(DataConsumerCommand::kDone));
loader->SetClient(consumer);
loader->SetHandle(std::move(src));
BlobBytesConsumerTestClient* client = new BlobBytesConsumerTestClient();
consumer->SetClient(client);
const char* buffer = nullptr;
size_t available;
ASSERT_EQ(Result::kOk, consumer->BeginRead(&buffer, &available));
EXPECT_TRUE(loader->IsStarted());
ASSERT_EQ(7u, available);
EXPECT_EQ("hello, ", String(buffer, available));
EXPECT_EQ(0, client->NumOnStateChangeCalled());
EXPECT_EQ(BytesConsumer::PublicState::kReadableOrWaiting,
consumer->GetPublicState());
} }
TEST_F(BlobBytesConsumerTest, ConstructedFromNullHandle) { TEST_F(BlobBytesConsumerTest, ConstructedFromNullHandle) {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h" #include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h" #include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
#include "third_party/blink/renderer/core/fetch/bytes_consumer_for_data_consumer_handle.h" #include "third_party/blink/renderer/core/fetch/bytes_consumer_for_data_consumer_handle.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h" #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
...@@ -319,7 +320,6 @@ class DataPipeAndDataBytesConsumer final : public BytesConsumer { ...@@ -319,7 +320,6 @@ class DataPipeAndDataBytesConsumer final : public BytesConsumer {
case PublicState::kErrored: case PublicState::kErrored:
return; return;
case PublicState::kClosed: case PublicState::kClosed:
NOTREACHED();
return; return;
case PublicState::kReadableOrWaiting: case PublicState::kReadableOrWaiting:
break; break;
......
...@@ -362,6 +362,18 @@ BlobPtr BlobDataHandle::CloneBlobPtr() { ...@@ -362,6 +362,18 @@ BlobPtr BlobDataHandle::CloneBlobPtr() {
return blob_clone; return blob_clone;
} }
network::mojom::blink::DataPipeGetterPtr BlobDataHandle::AsDataPipeGetter() {
MutexLocker locker(blob_info_mutex_);
if (!blob_info_.is_valid())
return nullptr;
network::mojom::blink::DataPipeGetterPtr result;
BlobPtr blob;
blob.Bind(std::move(blob_info_));
blob->AsDataPipeGetter(MakeRequest(&result));
blob_info_ = blob.PassInterface();
return result;
}
void BlobDataHandle::ReadAll(mojo::ScopedDataPipeProducerHandle pipe, void BlobDataHandle::ReadAll(mojo::ScopedDataPipeProducerHandle pipe,
mojom::blink::BlobReaderClientPtr client) { mojom::blink::BlobReaderClientPtr client) {
MutexLocker locker(blob_info_mutex_); MutexLocker locker(blob_info_mutex_);
......
...@@ -192,6 +192,7 @@ class PLATFORM_EXPORT BlobDataHandle ...@@ -192,6 +192,7 @@ class PLATFORM_EXPORT BlobDataHandle
~BlobDataHandle(); ~BlobDataHandle();
mojom::blink::BlobPtr CloneBlobPtr(); mojom::blink::BlobPtr CloneBlobPtr();
network::mojom::blink::DataPipeGetterPtr AsDataPipeGetter();
void ReadAll(mojo::ScopedDataPipeProducerHandle, void ReadAll(mojo::ScopedDataPipeProducerHandle,
mojom::blink::BlobReaderClientPtr); mojom::blink::BlobReaderClientPtr);
......
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
namespace blink { namespace blink {
class BlobBytesConsumer;
class BlobDataHandle; class BlobDataHandle;
class BlobURLRegistry; class BlobURLRegistry;
class KURL; class KURL;
...@@ -49,12 +48,11 @@ class PLATFORM_EXPORT BlobRegistry { ...@@ -49,12 +48,11 @@ class PLATFORM_EXPORT BlobRegistry {
STATIC_ONLY(BlobRegistry); STATIC_ONLY(BlobRegistry);
// Calling methods in this class directly won't work when Blob URL management // Calling methods in this class directly won't work when Blob URL management
// is switched to mojo. Instead codew should call PublicURLManager methods to // is switched to mojo. Instead code should call PublicURLManager methods to
// create/revoke blob URLs. // create/revoke blob URLs.
// To avoid new usage of these methods, mark all as private with friends for // To avoid new usage of these methods, mark all as private with friends for
// existing usage. // existing usage.
private: private:
friend class BlobBytesConsumer;
friend class BlobURLRegistry; friend class BlobURLRegistry;
// Methods for controlling Blob URLs. // Methods for controlling Blob URLs.
......
...@@ -5,19 +5,52 @@ ...@@ -5,19 +5,52 @@
#include "third_party/blink/renderer/platform/blob/testing/fake_blob.h" #include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "third_party/blink/public/platform/web_string.h"
namespace blink { namespace blink {
namespace {
FakeBlob::FakeBlob(const String& uuid) : uuid_(uuid) {} class SimpleDataPipeGetter : public network::mojom::blink::DataPipeGetter {
public:
SimpleDataPipeGetter(const String& str) : str_(str) {}
~SimpleDataPipeGetter() override = default;
// network::mojom::DataPipeGetter implementation:
void Read(mojo::ScopedDataPipeProducerHandle handle,
ReadCallback callback) override {
std::move(callback).Run(0 /* OK */, str_.length());
bool result = mojo::BlockingCopyFromString(WebString(str_).Utf8(), handle);
DCHECK(result);
}
void Clone(network::mojom::blink::DataPipeGetterRequest request) override {
mojo::MakeStrongBinding(std::make_unique<SimpleDataPipeGetter>(str_),
std::move(request));
}
private:
String str_;
DISALLOW_COPY_AND_ASSIGN(SimpleDataPipeGetter);
};
} // namespace
FakeBlob::FakeBlob(const String& uuid, const String& body, State* state)
: uuid_(uuid), body_(body), state_(state) {}
void FakeBlob::Clone(mojom::blink::BlobRequest request) { void FakeBlob::Clone(mojom::blink::BlobRequest request) {
mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid_), mojo::MakeStrongBinding(std::make_unique<FakeBlob>(uuid_, body_, state_),
std::move(request)); std::move(request));
} }
void FakeBlob::AsDataPipeGetter( void FakeBlob::AsDataPipeGetter(
network::mojom::blink::DataPipeGetterRequest request) { network::mojom::blink::DataPipeGetterRequest request) {
NOTREACHED(); if (state_)
state_->did_initiate_read_operation = true;
mojo::MakeStrongBinding(std::make_unique<SimpleDataPipeGetter>(body_),
std::move(request));
} }
void FakeBlob::ReadRange(uint64_t offset, void FakeBlob::ReadRange(uint64_t offset,
...@@ -27,9 +60,16 @@ void FakeBlob::ReadRange(uint64_t offset, ...@@ -27,9 +60,16 @@ void FakeBlob::ReadRange(uint64_t offset,
NOTREACHED(); NOTREACHED();
} }
void FakeBlob::ReadAll(mojo::ScopedDataPipeProducerHandle, void FakeBlob::ReadAll(mojo::ScopedDataPipeProducerHandle handle,
mojom::blink::BlobReaderClientPtr) { mojom::blink::BlobReaderClientPtr client) {
NOTREACHED(); if (state_)
state_->did_initiate_read_operation = true;
if (client)
client->OnCalculatedSize(body_.length(), body_.length());
bool result = mojo::BlockingCopyFromString(WebString(body_).Utf8(), handle);
DCHECK(result);
if (client)
client->OnComplete(0 /* OK */, body_.length());
} }
void FakeBlob::ReadSideData(ReadSideDataCallback callback) { void FakeBlob::ReadSideData(ReadSideDataCallback callback) {
......
...@@ -9,11 +9,17 @@ ...@@ -9,11 +9,17 @@
namespace blink { namespace blink {
// Mocked Blob implementation for testing. You can't read from a FakeBlob, but // Mocked Blob implementation for testing. Implements all methods except for
// it does have a UUID. // ReadRange and ReadSideData.
class FakeBlob : public mojom::blink::Blob { class FakeBlob : public mojom::blink::Blob {
public: public:
explicit FakeBlob(const String& uuid); struct State {
bool did_initiate_read_operation = false;
};
FakeBlob(const String& uuid,
const String& body = String(),
State* state = nullptr);
void Clone(mojom::blink::BlobRequest) override; void Clone(mojom::blink::BlobRequest) override;
void AsDataPipeGetter(network::mojom::blink::DataPipeGetterRequest) override; void AsDataPipeGetter(network::mojom::blink::DataPipeGetterRequest) override;
...@@ -25,9 +31,10 @@ class FakeBlob : public mojom::blink::Blob { ...@@ -25,9 +31,10 @@ class FakeBlob : public mojom::blink::Blob {
mojom::blink::BlobReaderClientPtr) override; mojom::blink::BlobReaderClientPtr) override;
void ReadSideData(ReadSideDataCallback) override; void ReadSideData(ReadSideDataCallback) override;
void GetInternalUUID(GetInternalUUIDCallback) override; void GetInternalUUID(GetInternalUUIDCallback) override;
private: private:
String uuid_; String uuid_;
String body_;
State* state_;
}; };
} // namespace blink } // namespace blink
......
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