blob: cc0775c1fbae7553acd6cf3b9603faecc54afc72 [file] [log] [blame]
// Copyright 2017 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 "storage/browser/blob/blob_impl.h"
#include <limits>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "net/base/io_buffer.h"
#include "net/disk_cache/disk_cache.h"
#include "storage/browser/blob/blob_data_handle.h"
#include "storage/browser/blob/blob_data_item.h"
#include "storage/browser/blob/blob_data_snapshot.h"
#include "storage/browser/blob/mojo_blob_reader.h"
namespace storage {
namespace {
class ReaderDelegate : public MojoBlobReader::Delegate {
public:
ReaderDelegate(blink::mojom::BlobReaderClientPtr client)
: client_(std::move(client)) {}
MojoBlobReader::Delegate::RequestSideData DidCalculateSize(
uint64_t total_size,
uint64_t content_size) override {
if (client_)
client_->OnCalculatedSize(total_size, content_size);
return MojoBlobReader::Delegate::DONT_REQUEST_SIDE_DATA;
}
void OnComplete(net::Error result, uint64_t total_written_bytes) override {
if (client_)
client_->OnComplete(result, total_written_bytes);
}
private:
blink::mojom::BlobReaderClientPtr client_;
DISALLOW_COPY_AND_ASSIGN(ReaderDelegate);
};
class DataPipeGetterReaderDelegate : public MojoBlobReader::Delegate {
public:
DataPipeGetterReaderDelegate(
network::mojom::DataPipeGetter::ReadCallback callback)
: callback_(std::move(callback)) {}
MojoBlobReader::Delegate::RequestSideData DidCalculateSize(
uint64_t total_size,
uint64_t content_size) override {
// Check if null since it's conceivable OnComplete() was already called
// with error.
if (!callback_.is_null())
std::move(callback_).Run(net::OK, content_size);
return MojoBlobReader::Delegate::DONT_REQUEST_SIDE_DATA;
}
void OnComplete(net::Error result, uint64_t total_written_bytes) override {
// Check if null since DidCalculateSize() may have already been called
// and an error occurred later.
if (!callback_.is_null() && result != net::OK) {
// On error, signal failure immediately. On success, OnCalculatedSize()
// is guaranteed to be called, and the result will be signaled from
// there.
std::move(callback_).Run(result, 0);
}
}
private:
network::mojom::DataPipeGetter::ReadCallback callback_;
DISALLOW_COPY_AND_ASSIGN(DataPipeGetterReaderDelegate);
};
} // namespace
// static
base::WeakPtr<BlobImpl> BlobImpl::Create(std::unique_ptr<BlobDataHandle> handle,
blink::mojom::BlobRequest request) {
return (new BlobImpl(std::move(handle), std::move(request)))
->weak_ptr_factory_.GetWeakPtr();
}
void BlobImpl::Clone(blink::mojom::BlobRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void BlobImpl::AsDataPipeGetter(network::mojom::DataPipeGetterRequest request) {
data_pipe_getter_bindings_.AddBinding(this, std::move(request));
}
void BlobImpl::ReadRange(uint64_t offset,
uint64_t length,
mojo::ScopedDataPipeProducerHandle handle,
blink::mojom::BlobReaderClientPtr client) {
MojoBlobReader::Create(
handle_.get(),
(length == std::numeric_limits<uint64_t>::max())
? net::HttpByteRange::RightUnbounded(offset)
: net::HttpByteRange::Bounded(offset, offset + length - 1),
std::make_unique<ReaderDelegate>(std::move(client)), std::move(handle));
}
void BlobImpl::ReadAll(mojo::ScopedDataPipeProducerHandle handle,
blink::mojom::BlobReaderClientPtr client) {
MojoBlobReader::Create(handle_.get(), net::HttpByteRange(),
std::make_unique<ReaderDelegate>(std::move(client)),
std::move(handle));
}
void BlobImpl::ReadSideData(ReadSideDataCallback callback) {
handle_->RunOnConstructionComplete(base::BindOnce(
[](BlobDataHandle handle, ReadSideDataCallback callback,
BlobStatus status) {
if (status != BlobStatus::DONE) {
std::move(callback).Run(base::nullopt);
return;
}
auto snapshot = handle.CreateSnapshot();
// Currently side data is supported only for single DiskCache entry
// blob.
const auto& items = snapshot->items();
if (items.size() != 1 ||
items[0]->type() != BlobDataItem::Type::kDiskCacheEntry) {
std::move(callback).Run(base::nullopt);
return;
}
const auto& item = items[0];
disk_cache::Entry* entry = item->disk_cache_entry();
if (!entry) {
std::move(callback).Run(base::nullopt);
return;
}
int32_t body_size =
entry->GetDataSize(item->disk_cache_side_stream_index());
if (body_size == 0) {
std::move(callback).Run(std::vector<uint8_t>());
return;
}
auto io_buffer = base::MakeRefCounted<net::IOBufferWithSize>(body_size);
auto io_callback = base::AdaptCallbackForRepeating(base::BindOnce(
[](scoped_refptr<net::IOBufferWithSize> io_buffer,
ReadSideDataCallback callback, int result) {
if (result < 0) {
std::move(callback).Run(base::nullopt);
return;
}
const uint8_t* data =
reinterpret_cast<const uint8_t*>(io_buffer->data());
std::move(callback).Run(
std::vector<uint8_t>(data, data + io_buffer->size()));
},
io_buffer, std::move(callback)));
int rv = entry->ReadData(item->disk_cache_side_stream_index(), 0,
io_buffer.get(), body_size, io_callback);
if (rv != net::ERR_IO_PENDING)
io_callback.Run(rv);
},
*handle_, std::move(callback)));
}
void BlobImpl::GetInternalUUID(GetInternalUUIDCallback callback) {
std::move(callback).Run(handle_->uuid());
}
void BlobImpl::Clone(network::mojom::DataPipeGetterRequest request) {
data_pipe_getter_bindings_.AddBinding(this, std::move(request));
}
void BlobImpl::Read(mojo::ScopedDataPipeProducerHandle handle,
ReadCallback callback) {
MojoBlobReader::Create(
handle_.get(), net::HttpByteRange(),
std::make_unique<DataPipeGetterReaderDelegate>(std::move(callback)),
std::move(handle));
}
void BlobImpl::FlushForTesting() {
bindings_.FlushForTesting();
data_pipe_getter_bindings_.FlushForTesting();
if (bindings_.empty() && data_pipe_getter_bindings_.empty())
delete this;
}
BlobImpl::BlobImpl(std::unique_ptr<BlobDataHandle> handle,
blink::mojom::BlobRequest request)
: handle_(std::move(handle)), weak_ptr_factory_(this) {
DCHECK(handle_);
bindings_.AddBinding(this, std::move(request));
bindings_.set_connection_error_handler(base::BindRepeating(
&BlobImpl::OnConnectionError, base::Unretained(this)));
data_pipe_getter_bindings_.set_connection_error_handler(base::BindRepeating(
&BlobImpl::OnConnectionError, base::Unretained(this)));
}
BlobImpl::~BlobImpl() = default;
void BlobImpl::OnConnectionError() {
if (!bindings_.empty())
return;
if (!data_pipe_getter_bindings_.empty())
return;
delete this;
}
} // namespace storage