blob: 37129f58b35d8ea938a2c518703475c75a401c76 [file] [log] [blame]
// Copyright 2019 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 "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h"
#include "base/single_thread_task_runner.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace blink {
ReplayingBytesConsumer::ReplayingBytesConsumer(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
ReplayingBytesConsumer::~ReplayingBytesConsumer() {}
BytesConsumer::Result ReplayingBytesConsumer::BeginRead(const char** buffer,
size_t* available) {
DCHECK(!is_in_two_phase_read_);
++notification_token_;
if (commands_.IsEmpty()) {
switch (state_) {
case BytesConsumer::InternalState::kReadable:
case BytesConsumer::InternalState::kWaiting:
return Result::kShouldWait;
case BytesConsumer::InternalState::kClosed:
return Result::kDone;
case BytesConsumer::InternalState::kErrored:
return Result::kError;
}
}
const Command& command = commands_[0];
switch (command.GetName()) {
case Command::kDataAndDone:
case Command::kData:
DCHECK_LE(offset_, command.Body().size());
*buffer = command.Body().data() + offset_;
*available = command.Body().size() - offset_;
is_in_two_phase_read_ = true;
return Result::kOk;
case Command::kDone:
commands_.pop_front();
Close();
return Result::kDone;
case Command::kError: {
Error e(String::FromUTF8(command.Body().data(), command.Body().size()));
commands_.pop_front();
MakeErrored(std::move(e));
return Result::kError;
}
case Command::kWait:
commands_.pop_front();
state_ = InternalState::kWaiting;
task_runner_->PostTask(
FROM_HERE, WTF::Bind(&ReplayingBytesConsumer::NotifyAsReadable,
WrapPersistent(this), notification_token_));
return Result::kShouldWait;
}
NOTREACHED();
return Result::kError;
}
BytesConsumer::Result ReplayingBytesConsumer::EndRead(size_t read) {
DCHECK(is_in_two_phase_read_);
DCHECK(!commands_.IsEmpty());
is_in_two_phase_read_ = false;
const Command& command = commands_[0];
const auto name = command.GetName();
DCHECK(name == Command::kData || name == Command::kDataAndDone);
offset_ += read;
DCHECK_LE(offset_, command.Body().size());
if (offset_ < command.Body().size())
return Result::kOk;
offset_ = 0;
commands_.pop_front();
if (name == Command::kData)
return Result::kOk;
Close();
return Result::kDone;
}
void ReplayingBytesConsumer::SetClient(Client* client) {
DCHECK(!client_);
DCHECK(client);
client_ = client;
++notification_token_;
}
void ReplayingBytesConsumer::ClearClient() {
DCHECK(client_);
client_ = nullptr;
++notification_token_;
}
void ReplayingBytesConsumer::Cancel() {
Close();
is_cancelled_ = true;
}
BytesConsumer::PublicState ReplayingBytesConsumer::GetPublicState() const {
return GetPublicStateFromInternalState(state_);
}
BytesConsumer::Error ReplayingBytesConsumer::GetError() const {
return error_;
}
void ReplayingBytesConsumer::NotifyAsReadable(int notification_token) {
if (notification_token_ != notification_token) {
// The notification is cancelled.
return;
}
DCHECK(client_);
DCHECK_NE(InternalState::kClosed, state_);
DCHECK_NE(InternalState::kErrored, state_);
client_->OnStateChange();
}
void ReplayingBytesConsumer::Close() {
commands_.clear();
offset_ = 0;
state_ = InternalState::kClosed;
++notification_token_;
}
void ReplayingBytesConsumer::MakeErrored(const Error& e) {
commands_.clear();
offset_ = 0;
error_ = e;
state_ = InternalState::kErrored;
++notification_token_;
}
void ReplayingBytesConsumer::Trace(Visitor* visitor) const {
visitor->Trace(client_);
BytesConsumer::Trace(visitor);
}
} // namespace blink