| // 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 |