blob: 6c332f6295c247ad920071a9627f212756c66c61 [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 "content/browser/cache_storage/cache_storage_cache_entry_handler.h"
#include "base/guid.h"
#include "base/optional.h"
#include "base/task/post_task.h"
#include "content/browser/background_fetch/storage/cache_entry_handler_impl.h"
#include "content/browser/cache_storage/cache_storage_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_impl.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "third_party/blink/public/common/blob/blob_utils.h"
namespace content {
namespace {
// A |BlobDataItem::DataHandle| implementation that wraps a
// |DiskCacheBlobEntry|. In addition, each |DataHandleImpl| maps the main
// and side data to particular disk_cache indices.
//
// The |DataHandleImpl| is a "readable" handle. It overrides the virtual
// size and reading methods to access the underlying disk_cache entry.
class DataHandleImpl : public storage::BlobDataItem::DataHandle {
public:
DataHandleImpl(
scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
blob_entry,
CacheStorageCache::EntryIndex disk_cache_index,
CacheStorageCache::EntryIndex side_data_disk_cache_index)
: blob_entry_(std::move(blob_entry)),
disk_cache_index_(disk_cache_index),
side_data_disk_cache_index_(side_data_disk_cache_index) {}
uint64_t GetSize() const override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return blob_entry_->GetSize(disk_cache_index_);
}
int Read(scoped_refptr<net::IOBuffer> dst_buffer,
uint64_t src_offset,
int bytes_to_read,
base::OnceCallback<void(int)> callback) override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return blob_entry_->Read(std::move(dst_buffer), disk_cache_index_,
src_offset, bytes_to_read, std::move(callback));
}
uint64_t GetSideDataSize() const override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (side_data_disk_cache_index_ == CacheStorageCache::INDEX_INVALID)
return 0;
return blob_entry_->GetSize(side_data_disk_cache_index_);
}
int ReadSideData(scoped_refptr<net::IOBuffer> dst_buffer,
base::OnceCallback<void(int)> callback) override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (side_data_disk_cache_index_ == CacheStorageCache::INDEX_INVALID)
return net::ERR_FAILED;
return blob_entry_->Read(std::move(dst_buffer), side_data_disk_cache_index_,
/* offset= */ 0, GetSideDataSize(),
std::move(callback));
}
void PrintTo(::std::ostream* os) const override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
blob_entry_->PrintTo(os);
*os << ",disk_cache_index:" << disk_cache_index_;
}
const char* BytesReadHistogramLabel() const override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return "DiskCache.CacheStorage";
}
private:
~DataHandleImpl() override = default;
const scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
blob_entry_;
const CacheStorageCache::EntryIndex disk_cache_index_;
const CacheStorageCache::EntryIndex side_data_disk_cache_index_;
DISALLOW_COPY_AND_ASSIGN(DataHandleImpl);
};
void FinalizeBlobOnIOThread(
base::WeakPtr<storage::BlobStorageContext> blob_context,
scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry> blob_entry,
CacheStorageCache::EntryIndex disk_cache_index,
CacheStorageCache::EntryIndex side_data_disk_cache_index,
std::string uuid,
mojo::PendingReceiver<blink::mojom::Blob> receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Just allow the blob mojo message pipe to automatically close if we're
// shutting down.
if (!blob_context)
return;
auto inner_handle = base::MakeRefCounted<DataHandleImpl>(
std::move(blob_entry), disk_cache_index, side_data_disk_cache_index);
auto blob_data = std::make_unique<storage::BlobDataBuilder>(uuid);
blob_data->AppendReadableDataHandle(std::move(inner_handle));
auto blob_handle = blob_context->AddFinishedBlob(std::move(blob_data));
storage::BlobImpl::Create(std::move(blob_handle), std::move(receiver));
}
} // namespace
CacheStorageCacheEntryHandler::DiskCacheBlobEntry::DiskCacheBlobEntry(
util::PassKey<CacheStorageCacheEntryHandler> key,
base::WeakPtr<CacheStorageCacheEntryHandler> entry_handler,
CacheStorageCacheHandle cache_handle,
disk_cache::ScopedEntryPtr disk_cache_entry)
: base::RefCountedDeleteOnSequence<DiskCacheBlobEntry>(
base::SequencedTaskRunnerHandle::Get()),
task_runner_(base::SequencedTaskRunnerHandle::Get()),
entry_handler_(std::move(entry_handler)),
cache_handle_(std::move(cache_handle)),
disk_cache_entry_(std::move(disk_cache_entry)),
valid_(true),
key_(disk_cache_entry_->GetKey()),
index_headers_size_(
disk_cache_entry_->GetDataSize(CacheStorageCache::INDEX_HEADERS)),
index_response_body_size_(disk_cache_entry_->GetDataSize(
CacheStorageCache::INDEX_RESPONSE_BODY)),
index_side_data_size_(
disk_cache_entry_->GetDataSize(CacheStorageCache::INDEX_SIDE_DATA)) {}
int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::Read(
scoped_refptr<net::IOBuffer> dst_buffer,
CacheStorageCache::EntryIndex disk_cache_index,
uint64_t offset,
int bytes_to_read,
base::OnceCallback<void(int)> callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!valid_)
return net::ERR_CACHE_READ_FAILURE;
if (task_runner_->RunsTasksInCurrentSequence()) {
return ReadOnSequenceInternal(std::move(dst_buffer), disk_cache_index,
offset, bytes_to_read, std::move(callback));
}
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&DiskCacheBlobEntry::ReadOnSequence, this,
std::move(dst_buffer), disk_cache_index, offset,
bytes_to_read, std::move(callback)));
return net::ERR_IO_PENDING;
}
int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::GetSize(
CacheStorageCache::EntryIndex disk_cache_index) const {
// Callable on any thread.
if (!valid_)
return 0;
switch (disk_cache_index) {
case CacheStorageCache::INDEX_INVALID:
return 0;
case CacheStorageCache::INDEX_HEADERS:
return index_headers_size_;
case CacheStorageCache::INDEX_RESPONSE_BODY:
return index_response_body_size_;
case CacheStorageCache::INDEX_SIDE_DATA:
return index_side_data_size_;
}
NOTREACHED();
}
void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::PrintTo(
::std::ostream* os) const {
// Callable on any thread.
if (valid_)
*os << "disk_cache_key:" << key_;
else
*os << "<invalidated>";
}
void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::Invalidate() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
valid_ = false;
cache_handle_ = base::nullopt;
entry_handler_ = nullptr;
disk_cache_entry_ = nullptr;
}
disk_cache::ScopedEntryPtr&
CacheStorageCacheEntryHandler::DiskCacheBlobEntry::disk_cache_entry() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return disk_cache_entry_;
}
void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::ReadOnSequence(
scoped_refptr<net::IOBuffer> dst_buffer,
int disk_cache_index,
uint64_t offset,
int bytes_to_read,
base::OnceCallback<void(int)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The disk_cache behavior of sometimes returning the result synchronously
// and sometimes invoking the callback requires us to adapt our callback
// to repeating here. Ideally disk_cache should be refactored to always
// report the result through the callback.
auto adapted_callback = base::AdaptCallbackForRepeating(std::move(callback));
int result = ReadOnSequenceInternal(std::move(dst_buffer), disk_cache_index,
offset, bytes_to_read, adapted_callback);
if (result == net::ERR_IO_PENDING)
return;
DidReadOnSequence(std::move(adapted_callback), result);
}
int CacheStorageCacheEntryHandler::DiskCacheBlobEntry::ReadOnSequenceInternal(
scoped_refptr<net::IOBuffer> dst_buffer,
int disk_cache_index,
uint64_t offset,
int bytes_to_read,
base::OnceCallback<void(int)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!disk_cache_entry_) {
return net::ERR_CACHE_READ_FAILURE;
}
return disk_cache_entry_->ReadData(
disk_cache_index, offset, dst_buffer.get(), bytes_to_read,
base::BindOnce(&DiskCacheBlobEntry::DidReadOnSequence, this,
std::move(callback)));
}
void CacheStorageCacheEntryHandler::DiskCacheBlobEntry::DidReadOnSequence(
base::OnceCallback<void(int)> callback,
int result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
std::move(callback).Run(result);
} else {
base::PostTask(FROM_HERE, {BrowserThread::IO},
base::BindOnce(std::move(callback), result));
}
}
CacheStorageCacheEntryHandler::DiskCacheBlobEntry::~DiskCacheBlobEntry() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (entry_handler_)
entry_handler_->EraseDiskCacheBlobEntry(this);
}
PutContext::PutContext(blink::mojom::FetchAPIRequestPtr request,
blink::mojom::FetchAPIResponsePtr response,
mojo::PendingRemote<blink::mojom::Blob> blob,
uint64_t blob_size,
mojo::PendingRemote<blink::mojom::Blob> side_data_blob,
uint64_t side_data_blob_size,
int64_t trace_id)
: request(std::move(request)),
response(std::move(response)),
blob(std::move(blob)),
blob_size(blob_size),
side_data_blob(std::move(side_data_blob)),
side_data_blob_size(side_data_blob_size),
trace_id(trace_id) {}
PutContext::~PutContext() = default;
// Default implemetation of CacheStorageCacheEntryHandler.
class CacheStorageCacheEntryHandlerImpl : public CacheStorageCacheEntryHandler {
public:
CacheStorageCacheEntryHandlerImpl(
base::WeakPtr<storage::BlobStorageContext> blob_context)
: CacheStorageCacheEntryHandler(std::move(blob_context)) {}
~CacheStorageCacheEntryHandlerImpl() override = default;
std::unique_ptr<PutContext> CreatePutContext(
blink::mojom::FetchAPIRequestPtr request,
blink::mojom::FetchAPIResponsePtr response,
int64_t trace_id) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
mojo::PendingRemote<blink::mojom::Blob> blob;
uint64_t blob_size = blink::BlobUtils::kUnknownSize;
mojo::PendingRemote<blink::mojom::Blob> side_data_blob;
uint64_t side_data_blob_size = blink::BlobUtils::kUnknownSize;
if (response->blob) {
blob = std::move(response->blob->blob);
blob_size = response->blob->size;
}
if (response->side_data_blob_for_cache_put) {
side_data_blob = std::move(response->side_data_blob_for_cache_put->blob);
side_data_blob_size = response->side_data_blob_for_cache_put->size;
}
return std::make_unique<PutContext>(
std::move(request), std::move(response), std::move(blob), blob_size,
std::move(side_data_blob), side_data_blob_size, trace_id);
}
void PopulateResponseBody(scoped_refptr<DiskCacheBlobEntry> blob_entry,
blink::mojom::FetchAPIResponse* response) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// First create the blob and store it in the field for the main body
// loading.
response->blob = CreateBlobWithSideData(
std::move(blob_entry), CacheStorageCache::INDEX_RESPONSE_BODY,
CacheStorageCache::INDEX_SIDE_DATA);
// Then clone the blob to the |side_data_blob| field for loading code_cache.
mojo::Remote<blink::mojom::Blob> blob_remote(
std::move(response->blob->blob));
blob_remote->Clone(response->blob->blob.InitWithNewPipeAndPassReceiver());
response->side_data_blob = blink::mojom::SerializedBlob::New(
response->blob->uuid, response->blob->content_type,
response->blob->size, blob_remote.Unbind());
}
void PopulateRequestBody(scoped_refptr<DiskCacheBlobEntry> blob_entry,
blink::mojom::FetchAPIRequest* request) override {}
private:
base::WeakPtr<CacheStorageCacheEntryHandler> GetWeakPtr() override {
return weak_ptr_factory_.GetWeakPtr();
}
base::WeakPtrFactory<CacheStorageCacheEntryHandlerImpl> weak_ptr_factory_{
this};
};
CacheStorageCacheEntryHandler::CacheStorageCacheEntryHandler(
base::WeakPtr<storage::BlobStorageContext> blob_context)
: blob_context_(blob_context) {}
scoped_refptr<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>
CacheStorageCacheEntryHandler::CreateDiskCacheBlobEntry(
CacheStorageCacheHandle cache_handle,
disk_cache::ScopedEntryPtr disk_cache_entry) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto blob_entry =
base::MakeRefCounted<CacheStorageCacheEntryHandler::DiskCacheBlobEntry>(
util::PassKey<CacheStorageCacheEntryHandler>(), GetWeakPtr(),
std::move(cache_handle), std::move(disk_cache_entry));
DCHECK_EQ(blob_entries_.count(blob_entry.get()), 0u);
blob_entries_.insert(blob_entry.get());
return blob_entry;
}
CacheStorageCacheEntryHandler::~CacheStorageCacheEntryHandler() = default;
void CacheStorageCacheEntryHandler::InvalidateDiskCacheBlobEntrys() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Calling Invalidate() can cause the CacheStorageCacheEntryHandler to be
// destroyed. Be careful not to touch |this| after calling Invalidate().
std::set<DiskCacheBlobEntry*> entries = std::move(blob_entries_);
for (auto* entry : entries)
entry->Invalidate();
}
void CacheStorageCacheEntryHandler::EraseDiskCacheBlobEntry(
DiskCacheBlobEntry* blob_entry) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(blob_entries_.count(blob_entry), 0u);
blob_entries_.erase(blob_entry);
}
// static
std::unique_ptr<CacheStorageCacheEntryHandler>
CacheStorageCacheEntryHandler::CreateCacheEntryHandler(
CacheStorageOwner owner,
base::WeakPtr<storage::BlobStorageContext> blob_context) {
switch (owner) {
case CacheStorageOwner::kCacheAPI:
return std::make_unique<CacheStorageCacheEntryHandlerImpl>(
std::move(blob_context));
case CacheStorageOwner::kBackgroundFetch:
return std::make_unique<background_fetch::CacheEntryHandlerImpl>(
std::move(blob_context));
}
NOTREACHED();
}
blink::mojom::SerializedBlobPtr CacheStorageCacheEntryHandler::CreateBlob(
scoped_refptr<DiskCacheBlobEntry> blob_entry,
CacheStorageCache::EntryIndex disk_cache_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return CreateBlobWithSideData(std::move(blob_entry), disk_cache_index,
CacheStorageCache::INDEX_INVALID);
}
blink::mojom::SerializedBlobPtr
CacheStorageCacheEntryHandler::CreateBlobWithSideData(
scoped_refptr<DiskCacheBlobEntry> blob_entry,
CacheStorageCache::EntryIndex disk_cache_index,
CacheStorageCache::EntryIndex side_data_disk_cache_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto blob = blink::mojom::SerializedBlob::New();
blob->size = blob_entry->GetSize(disk_cache_index);
blob->uuid = base::GenerateGUID();
if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
FinalizeBlobOnIOThread(blob_context_, std::move(blob_entry),
disk_cache_index, side_data_disk_cache_index,
blob->uuid,
blob->blob.InitWithNewPipeAndPassReceiver());
} else {
base::PostTask(FROM_HERE, {BrowserThread::IO},
base::BindOnce(&FinalizeBlobOnIOThread, blob_context_,
std::move(blob_entry), disk_cache_index,
side_data_disk_cache_index, blob->uuid,
blob->blob.InitWithNewPipeAndPassReceiver()));
}
return blob;
}
} // namespace content