blob: 438e3b97d15b1dc25a0e2f22e005c005da78923b [file] [log] [blame]
// 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 "modules/fetch/DataConsumerHandleTestUtil.h"
#include <memory>
#include "bindings/core/v8/DOMWrapperWorld.h"
#include "platform/wtf/PtrUtil.h"
#include "public/platform/WebScheduler.h"
namespace blink {
using Result = WebDataConsumerHandle::Result;
namespace {
class WaitingHandle final : public WebDataConsumerHandle {
private:
class ReaderImpl final : public WebDataConsumerHandle::Reader {
public:
Result BeginRead(const void** buffer,
WebDataConsumerHandle::Flags,
size_t* available) override {
*available = 0;
*buffer = nullptr;
return kShouldWait;
}
Result EndRead(size_t) override { return kUnexpectedError; }
};
std::unique_ptr<Reader> ObtainReader(Client*) override {
return WTF::WrapUnique(new ReaderImpl);
}
const char* DebugName() const override { return "WaitingHandle"; }
};
} // namespace
DataConsumerHandleTestUtil::Thread::Thread(
const char* name,
InitializationPolicy initialization_policy)
: thread_(WebThreadSupportingGC::Create(name)),
initialization_policy_(initialization_policy),
waitable_event_(WTF::MakeUnique<WaitableEvent>()) {
thread_->PostTask(
BLINK_FROM_HERE,
CrossThreadBind(&Thread::Initialize, CrossThreadUnretained(this)));
waitable_event_->Wait();
}
DataConsumerHandleTestUtil::Thread::~Thread() {
thread_->PostTask(
BLINK_FROM_HERE,
CrossThreadBind(&Thread::Shutdown, CrossThreadUnretained(this)));
waitable_event_->Wait();
}
void DataConsumerHandleTestUtil::Thread::Initialize() {
if (initialization_policy_ >= kScriptExecution) {
isolate_holder_ =
WTF::MakeUnique<gin::IsolateHolder>(Platform::Current()
->CurrentThread()
->Scheduler()
->LoadingTaskRunner()
->ToSingleThreadTaskRunner());
GetIsolate()->Enter();
}
thread_->Initialize();
if (initialization_policy_ >= kScriptExecution) {
v8::HandleScope handle_scope(GetIsolate());
script_state_ = ScriptState::Create(
v8::Context::New(GetIsolate()),
DOMWrapperWorld::Create(GetIsolate(),
DOMWrapperWorld::WorldType::kTesting));
}
if (initialization_policy_ >= kWithExecutionContext) {
execution_context_ = new NullExecutionContext();
}
waitable_event_->Signal();
}
void DataConsumerHandleTestUtil::Thread::Shutdown() {
execution_context_ = nullptr;
if (script_state_) {
script_state_->DisposePerContextData();
}
script_state_ = nullptr;
thread_->Shutdown();
if (isolate_holder_) {
GetIsolate()->Exit();
GetIsolate()->RequestGarbageCollectionForTesting(
GetIsolate()->kFullGarbageCollection);
isolate_holder_ = nullptr;
}
waitable_event_->Signal();
}
class DataConsumerHandleTestUtil::ReplayingHandle::ReaderImpl final
: public Reader {
public:
ReaderImpl(PassRefPtr<Context> context, Client* client) : context_(context) {
context_->AttachReader(client);
}
~ReaderImpl() { context_->DetachReader(); }
Result BeginRead(const void** buffer,
Flags flags,
size_t* available) override {
return context_->BeginRead(buffer, flags, available);
}
Result EndRead(size_t read_size) override {
return context_->EndRead(read_size);
}
private:
RefPtr<Context> context_;
};
void DataConsumerHandleTestUtil::ReplayingHandle::Context::Add(
const Command& command) {
MutexLocker locker(mutex_);
commands_.push_back(command);
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::AttachReader(
WebDataConsumerHandle::Client* client) {
MutexLocker locker(mutex_);
DCHECK(!reader_thread_);
DCHECK(!client_);
reader_thread_ = Platform::Current()->CurrentThread();
client_ = client;
if (client_ && !(IsEmpty() && result_ == kShouldWait))
Notify();
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::DetachReader() {
MutexLocker locker(mutex_);
DCHECK(reader_thread_ && reader_thread_->IsCurrentThread());
reader_thread_ = nullptr;
client_ = nullptr;
if (!is_handle_attached_)
detached_->Signal();
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::DetachHandle() {
MutexLocker locker(mutex_);
is_handle_attached_ = false;
if (!reader_thread_)
detached_->Signal();
}
WebDataConsumerHandle::Result
DataConsumerHandleTestUtil::ReplayingHandle::Context::BeginRead(
const void** buffer,
Flags,
size_t* available) {
MutexLocker locker(mutex_);
*buffer = nullptr;
*available = 0;
if (IsEmpty())
return result_;
const Command& command = Top();
Result result = kOk;
switch (command.GetName()) {
case Command::kData: {
auto& body = command.Body();
*available = body.size() - Offset();
*buffer = body.Data() + Offset();
result = kOk;
break;
}
case Command::kDone:
result_ = result = kDone;
Consume(0);
break;
case Command::kWait:
Consume(0);
result = kShouldWait;
Notify();
break;
case Command::kError:
result_ = result = kUnexpectedError;
Consume(0);
break;
}
return result;
}
WebDataConsumerHandle::Result
DataConsumerHandleTestUtil::ReplayingHandle::Context::EndRead(
size_t read_size) {
MutexLocker locker(mutex_);
Consume(read_size);
return kOk;
}
DataConsumerHandleTestUtil::ReplayingHandle::Context::Context()
: offset_(0),
reader_thread_(nullptr),
client_(nullptr),
result_(kShouldWait),
is_handle_attached_(true),
detached_(WTF::MakeUnique<WaitableEvent>()) {}
const DataConsumerHandleTestUtil::Command&
DataConsumerHandleTestUtil::ReplayingHandle::Context::Top() {
DCHECK(!IsEmpty());
return commands_.front();
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::Consume(
size_t size) {
DCHECK(!IsEmpty());
DCHECK(size + offset_ <= Top().Body().size());
bool fully_consumed = (size + offset_ >= Top().Body().size());
if (fully_consumed) {
offset_ = 0;
commands_.pop_front();
} else {
offset_ += size;
}
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::Notify() {
if (!client_)
return;
DCHECK(reader_thread_);
reader_thread_->GetWebTaskRunner()->PostTask(
BLINK_FROM_HERE,
CrossThreadBind(&Context::NotifyInternal, WrapPassRefPtr(this)));
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::NotifyInternal() {
{
MutexLocker locker(mutex_);
if (!client_ || !reader_thread_->IsCurrentThread()) {
// There is no client, or a new reader is attached.
return;
}
}
// The reading thread is the current thread.
client_->DidGetReadable();
}
DataConsumerHandleTestUtil::ReplayingHandle::ReplayingHandle()
: context_(Context::Create()) {}
DataConsumerHandleTestUtil::ReplayingHandle::~ReplayingHandle() {
context_->DetachHandle();
}
std::unique_ptr<WebDataConsumerHandle::Reader>
DataConsumerHandleTestUtil::ReplayingHandle::ObtainReader(Client* client) {
return WTF::MakeUnique<ReaderImpl>(context_, client);
}
void DataConsumerHandleTestUtil::ReplayingHandle::Add(const Command& command) {
context_->Add(command);
}
DataConsumerHandleTestUtil::HandleReader::HandleReader(
std::unique_ptr<WebDataConsumerHandle> handle,
std::unique_ptr<OnFinishedReading> on_finished_reading)
: reader_(handle->ObtainReader(this)),
on_finished_reading_(std::move(on_finished_reading)) {}
void DataConsumerHandleTestUtil::HandleReader::DidGetReadable() {
WebDataConsumerHandle::Result r = WebDataConsumerHandle::kUnexpectedError;
char buffer[3];
while (true) {
size_t size;
r = reader_->Read(buffer, sizeof(buffer), WebDataConsumerHandle::kFlagNone,
&size);
if (r == WebDataConsumerHandle::kShouldWait)
return;
if (r != WebDataConsumerHandle::kOk)
break;
data_.Append(buffer, size);
}
std::unique_ptr<HandleReadResult> result =
WTF::MakeUnique<HandleReadResult>(r, data_);
data_.Clear();
Platform::Current()->CurrentThread()->GetWebTaskRunner()->PostTask(
BLINK_FROM_HERE,
WTF::Bind(&HandleReader::RunOnFinishedReading, WTF::Unretained(this),
WTF::Passed(std::move(result))));
reader_ = nullptr;
}
void DataConsumerHandleTestUtil::HandleReader::RunOnFinishedReading(
std::unique_ptr<HandleReadResult> result) {
DCHECK(on_finished_reading_);
std::unique_ptr<OnFinishedReading> on_finished_reading(
std::move(on_finished_reading_));
(*on_finished_reading)(std::move(result));
}
DataConsumerHandleTestUtil::HandleTwoPhaseReader::HandleTwoPhaseReader(
std::unique_ptr<WebDataConsumerHandle> handle,
std::unique_ptr<OnFinishedReading> on_finished_reading)
: reader_(handle->ObtainReader(this)),
on_finished_reading_(std::move(on_finished_reading)) {}
void DataConsumerHandleTestUtil::HandleTwoPhaseReader::DidGetReadable() {
WebDataConsumerHandle::Result r = WebDataConsumerHandle::kUnexpectedError;
while (true) {
const void* buffer = nullptr;
size_t size;
r = reader_->BeginRead(&buffer, WebDataConsumerHandle::kFlagNone, &size);
if (r == WebDataConsumerHandle::kShouldWait)
return;
if (r != WebDataConsumerHandle::kOk)
break;
// Read smaller than available in order to test |endRead|.
size_t read_size =
std::min(size, std::max(size * 2 / 3, static_cast<size_t>(1)));
data_.Append(static_cast<const char*>(buffer), read_size);
reader_->EndRead(read_size);
}
std::unique_ptr<HandleReadResult> result =
WTF::MakeUnique<HandleReadResult>(r, data_);
data_.Clear();
Platform::Current()->CurrentThread()->GetWebTaskRunner()->PostTask(
BLINK_FROM_HERE,
WTF::Bind(&HandleTwoPhaseReader::RunOnFinishedReading,
WTF::Unretained(this), WTF::Passed(std::move(result))));
reader_ = nullptr;
}
void DataConsumerHandleTestUtil::HandleTwoPhaseReader::RunOnFinishedReading(
std::unique_ptr<HandleReadResult> result) {
DCHECK(on_finished_reading_);
std::unique_ptr<OnFinishedReading> on_finished_reading(
std::move(on_finished_reading_));
(*on_finished_reading)(std::move(result));
}
std::unique_ptr<WebDataConsumerHandle>
DataConsumerHandleTestUtil::CreateWaitingDataConsumerHandle() {
return WTF::WrapUnique(new WaitingHandle);
}
} // namespace blink