blob: d87337c362206c75ff60ec18f4001cff614681d1 [file] [log] [blame]
// Copyright 2018 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/core/fetch/buffering_bytes_consumer.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
namespace blink {
BufferingBytesConsumer::BufferingBytesConsumer(BytesConsumer* bytes_consumer)
: bytes_consumer_(bytes_consumer) {
bytes_consumer_->SetClient(this);
}
BufferingBytesConsumer::~BufferingBytesConsumer() = default;
BytesConsumer::Result BufferingBytesConsumer::BeginRead(const char** buffer,
size_t* available) {
if (buffer_.IsEmpty()) {
if (has_seen_error_)
return Result::kError;
if (has_seen_end_of_data_) {
ClearClient();
return Result::kDone;
}
BufferData();
if (has_seen_error_)
return Result::kError;
if (buffer_.IsEmpty())
return has_seen_end_of_data_ ? Result::kDone : Result::kShouldWait;
}
DCHECK_LT(offset_for_first_chunk_, buffer_[0].size());
*buffer = buffer_[0].data() + offset_for_first_chunk_;
*available = buffer_[0].size() - offset_for_first_chunk_;
return Result::kOk;
}
BytesConsumer::Result BufferingBytesConsumer::EndRead(size_t read_size) {
if (buffer_.IsEmpty()) {
DCHECK(has_seen_error_);
return Result::kError;
}
DCHECK_LE(offset_for_first_chunk_ + read_size, buffer_[0].size());
offset_for_first_chunk_ += read_size;
if (offset_for_first_chunk_ == buffer_[0].size()) {
offset_for_first_chunk_ = 0;
buffer_.pop_front();
}
if (buffer_.IsEmpty() && has_seen_end_of_data_) {
ClearClient();
return Result::kDone;
}
return Result::kOk;
}
scoped_refptr<BlobDataHandle> BufferingBytesConsumer::DrainAsBlobDataHandle(
BlobSizePolicy policy) {
return bytes_consumer_->DrainAsBlobDataHandle(policy);
}
scoped_refptr<EncodedFormData> BufferingBytesConsumer::DrainAsFormData() {
return bytes_consumer_->DrainAsFormData();
}
mojo::ScopedDataPipeConsumerHandle BufferingBytesConsumer::DrainAsDataPipe() {
// We intentionally return an empty handle here, because returning a DataPipe
// may activate back pressure.
return {};
}
void BufferingBytesConsumer::SetClient(BytesConsumer::Client* client) {
client_ = client;
}
void BufferingBytesConsumer::ClearClient() {
client_ = nullptr;
}
void BufferingBytesConsumer::Cancel() {
ClearClient();
bytes_consumer_->Cancel();
}
BytesConsumer::PublicState BufferingBytesConsumer::GetPublicState() const {
if (buffer_.IsEmpty())
return bytes_consumer_->GetPublicState();
return PublicState::kReadableOrWaiting;
}
BytesConsumer::Error BufferingBytesConsumer::GetError() const {
return bytes_consumer_->GetError();
}
void BufferingBytesConsumer::Trace(Visitor* visitor) {
visitor->Trace(bytes_consumer_);
visitor->Trace(client_);
BytesConsumer::Trace(visitor);
BytesConsumer::Client::Trace(visitor);
}
void BufferingBytesConsumer::OnStateChange() {
BytesConsumer::Client* client = client_;
BufferData();
if (client)
client->OnStateChange();
}
void BufferingBytesConsumer::BufferData() {
while (true) {
const char* p = nullptr;
size_t available = 0;
auto result = bytes_consumer_->BeginRead(&p, &available);
if (result == Result::kShouldWait)
return;
if (result == Result::kOk) {
Vector<char> chunk;
chunk.Append(p, SafeCast<wtf_size_t>(available));
buffer_.push_back(std::move(chunk));
result = bytes_consumer_->EndRead(available);
}
if (result == Result::kDone) {
has_seen_end_of_data_ = true;
ClearClient();
return;
}
if (result != Result::kOk) {
buffer_.clear();
has_seen_error_ = true;
ClearClient();
return;
}
}
}
} // namespace blink