blob: 9940a196650244f6a3f7548d809e0269c14c3c02 [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 "bindings/core/v8/DOMWrapperWorld.h"
#include "wtf/PtrUtil.h"
#include <memory>
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 ShouldWait;
}
Result endRead(size_t) override { return UnexpectedError; }
};
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 initializationPolicy)
: m_thread(
WebThreadSupportingGC::create(name, BlinkGC::MainThreadHeapMode)),
m_initializationPolicy(initializationPolicy),
m_waitableEvent(wrapUnique(new WaitableEvent())) {
m_thread->postTask(
BLINK_FROM_HERE,
crossThreadBind(&Thread::initialize, crossThreadUnretained(this)));
m_waitableEvent->wait();
}
DataConsumerHandleTestUtil::Thread::~Thread() {
m_thread->postTask(
BLINK_FROM_HERE,
crossThreadBind(&Thread::shutdown, crossThreadUnretained(this)));
m_waitableEvent->wait();
}
void DataConsumerHandleTestUtil::Thread::initialize() {
if (m_initializationPolicy >= ScriptExecution) {
m_isolateHolder = wrapUnique(new gin::IsolateHolder());
isolate()->Enter();
}
m_thread->initialize();
if (m_initializationPolicy >= ScriptExecution) {
v8::HandleScope handleScope(isolate());
m_scriptState = ScriptState::create(v8::Context::New(isolate()),
DOMWrapperWorld::create(isolate()));
}
if (m_initializationPolicy >= WithExecutionContext) {
m_executionContext = new NullExecutionContext();
}
m_waitableEvent->signal();
}
void DataConsumerHandleTestUtil::Thread::shutdown() {
m_executionContext = nullptr;
if (m_scriptState) {
m_scriptState->disposePerContextData();
}
m_scriptState = nullptr;
m_thread->shutdown();
if (m_isolateHolder) {
isolate()->Exit();
isolate()->RequestGarbageCollectionForTesting(
isolate()->kFullGarbageCollection);
m_isolateHolder = nullptr;
}
m_waitableEvent->signal();
}
class DataConsumerHandleTestUtil::ReplayingHandle::ReaderImpl final
: public Reader {
public:
ReaderImpl(PassRefPtr<Context> context, Client* client) : m_context(context) {
m_context->attachReader(client);
}
~ReaderImpl() { m_context->detachReader(); }
Result beginRead(const void** buffer,
Flags flags,
size_t* available) override {
return m_context->beginRead(buffer, flags, available);
}
Result endRead(size_t readSize) override {
return m_context->endRead(readSize);
}
private:
RefPtr<Context> m_context;
};
void DataConsumerHandleTestUtil::ReplayingHandle::Context::add(
const Command& command) {
MutexLocker locker(m_mutex);
m_commands.append(command);
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::attachReader(
WebDataConsumerHandle::Client* client) {
MutexLocker locker(m_mutex);
DCHECK(!m_readerThread);
DCHECK(!m_client);
m_readerThread = Platform::current()->currentThread();
m_client = client;
if (m_client && !(isEmpty() && m_result == ShouldWait))
notify();
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::detachReader() {
MutexLocker locker(m_mutex);
DCHECK(m_readerThread && m_readerThread->isCurrentThread());
m_readerThread = nullptr;
m_client = nullptr;
if (!m_isHandleAttached)
m_detached->signal();
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::detachHandle() {
MutexLocker locker(m_mutex);
m_isHandleAttached = false;
if (!m_readerThread)
m_detached->signal();
}
WebDataConsumerHandle::Result
DataConsumerHandleTestUtil::ReplayingHandle::Context::beginRead(
const void** buffer,
Flags,
size_t* available) {
MutexLocker locker(m_mutex);
*buffer = nullptr;
*available = 0;
if (isEmpty())
return m_result;
const Command& command = top();
Result result = Ok;
switch (command.getName()) {
case Command::Data: {
auto& body = command.body();
*available = body.size() - offset();
*buffer = body.data() + offset();
result = Ok;
break;
}
case Command::Done:
m_result = result = Done;
consume(0);
break;
case Command::Wait:
consume(0);
result = ShouldWait;
notify();
break;
case Command::Error:
m_result = result = UnexpectedError;
consume(0);
break;
}
return result;
}
WebDataConsumerHandle::Result
DataConsumerHandleTestUtil::ReplayingHandle::Context::endRead(size_t readSize) {
MutexLocker locker(m_mutex);
consume(readSize);
return Ok;
}
DataConsumerHandleTestUtil::ReplayingHandle::Context::Context()
: m_offset(0),
m_readerThread(nullptr),
m_client(nullptr),
m_result(ShouldWait),
m_isHandleAttached(true),
m_detached(wrapUnique(new WaitableEvent())) {}
const DataConsumerHandleTestUtil::Command&
DataConsumerHandleTestUtil::ReplayingHandle::Context::top() {
DCHECK(!isEmpty());
return m_commands.first();
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::consume(
size_t size) {
DCHECK(!isEmpty());
DCHECK(size + m_offset <= top().body().size());
bool fullyConsumed = (size + m_offset >= top().body().size());
if (fullyConsumed) {
m_offset = 0;
m_commands.removeFirst();
} else {
m_offset += size;
}
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::notify() {
if (!m_client)
return;
DCHECK(m_readerThread);
m_readerThread->getWebTaskRunner()->postTask(
BLINK_FROM_HERE,
crossThreadBind(&Context::notifyInternal, wrapPassRefPtr(this)));
}
void DataConsumerHandleTestUtil::ReplayingHandle::Context::notifyInternal() {
{
MutexLocker locker(m_mutex);
if (!m_client || !m_readerThread->isCurrentThread()) {
// There is no client, or a new reader is attached.
return;
}
}
// The reading thread is the current thread.
m_client->didGetReadable();
}
DataConsumerHandleTestUtil::ReplayingHandle::ReplayingHandle()
: m_context(Context::create()) {}
DataConsumerHandleTestUtil::ReplayingHandle::~ReplayingHandle() {
m_context->detachHandle();
}
std::unique_ptr<WebDataConsumerHandle::Reader>
DataConsumerHandleTestUtil::ReplayingHandle::obtainReader(Client* client) {
return WTF::wrapUnique(new ReaderImpl(m_context, client));
}
void DataConsumerHandleTestUtil::ReplayingHandle::add(const Command& command) {
m_context->add(command);
}
DataConsumerHandleTestUtil::HandleReader::HandleReader(
std::unique_ptr<WebDataConsumerHandle> handle,
std::unique_ptr<OnFinishedReading> onFinishedReading)
: m_reader(handle->obtainReader(this)),
m_onFinishedReading(std::move(onFinishedReading)) {}
void DataConsumerHandleTestUtil::HandleReader::didGetReadable() {
WebDataConsumerHandle::Result r = WebDataConsumerHandle::UnexpectedError;
char buffer[3];
while (true) {
size_t size;
r = m_reader->read(buffer, sizeof(buffer), WebDataConsumerHandle::FlagNone,
&size);
if (r == WebDataConsumerHandle::ShouldWait)
return;
if (r != WebDataConsumerHandle::Ok)
break;
m_data.append(buffer, size);
}
std::unique_ptr<HandleReadResult> result =
wrapUnique(new HandleReadResult(r, m_data));
m_data.clear();
Platform::current()->currentThread()->getWebTaskRunner()->postTask(
BLINK_FROM_HERE,
WTF::bind(&HandleReader::runOnFinishedReading, WTF::unretained(this),
passed(std::move(result))));
m_reader = nullptr;
}
void DataConsumerHandleTestUtil::HandleReader::runOnFinishedReading(
std::unique_ptr<HandleReadResult> result) {
DCHECK(m_onFinishedReading);
std::unique_ptr<OnFinishedReading> onFinishedReading(
std::move(m_onFinishedReading));
(*onFinishedReading)(std::move(result));
}
DataConsumerHandleTestUtil::HandleTwoPhaseReader::HandleTwoPhaseReader(
std::unique_ptr<WebDataConsumerHandle> handle,
std::unique_ptr<OnFinishedReading> onFinishedReading)
: m_reader(handle->obtainReader(this)),
m_onFinishedReading(std::move(onFinishedReading)) {}
void DataConsumerHandleTestUtil::HandleTwoPhaseReader::didGetReadable() {
WebDataConsumerHandle::Result r = WebDataConsumerHandle::UnexpectedError;
while (true) {
const void* buffer = nullptr;
size_t size;
r = m_reader->beginRead(&buffer, WebDataConsumerHandle::FlagNone, &size);
if (r == WebDataConsumerHandle::ShouldWait)
return;
if (r != WebDataConsumerHandle::Ok)
break;
// Read smaller than available in order to test |endRead|.
size_t readSize =
std::min(size, std::max(size * 2 / 3, static_cast<size_t>(1)));
m_data.append(static_cast<const char*>(buffer), readSize);
m_reader->endRead(readSize);
}
std::unique_ptr<HandleReadResult> result =
wrapUnique(new HandleReadResult(r, m_data));
m_data.clear();
Platform::current()->currentThread()->getWebTaskRunner()->postTask(
BLINK_FROM_HERE,
WTF::bind(&HandleTwoPhaseReader::runOnFinishedReading,
WTF::unretained(this), passed(std::move(result))));
m_reader = nullptr;
}
void DataConsumerHandleTestUtil::HandleTwoPhaseReader::runOnFinishedReading(
std::unique_ptr<HandleReadResult> result) {
DCHECK(m_onFinishedReading);
std::unique_ptr<OnFinishedReading> onFinishedReading(
std::move(m_onFinishedReading));
(*onFinishedReading)(std::move(result));
}
std::unique_ptr<WebDataConsumerHandle>
DataConsumerHandleTestUtil::createWaitingDataConsumerHandle() {
return wrapUnique(new WaitingHandle);
}
} // namespace blink