blob: 79cec0a95900a5e90e8523a21146f09e24a6b12e [file] [log] [blame]
// 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 "content/browser/cache_storage/cache_storage_context_impl.h"
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/cache_storage/cache_storage_dispatcher_host.h"
#include "content/browser/cache_storage/cache_storage_quota_client.h"
#include "content/browser/cache_storage/cross_sequence/cross_sequence_cache_storage_manager.h"
#include "content/browser/cache_storage/legacy/legacy_cache_storage_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/quota/special_storage_policy.h"
#include "url/origin.h"
namespace content {
namespace {
const base::Feature kCacheStorageSequenceFeature{
"CacheStorageSequence", base::FEATURE_ENABLED_BY_DEFAULT};
scoped_refptr<base::SequencedTaskRunner> CreateSchedulerTaskRunner() {
if (!base::FeatureList::IsEnabled(kCacheStorageSequenceFeature))
return base::CreateSingleThreadTaskRunner({BrowserThread::IO});
return base::CreateSequencedTaskRunner(
{base::ThreadPool(), base::TaskPriority::USER_VISIBLE});
}
} // namespace
CacheStorageContextImpl::CacheStorageContextImpl(
BrowserContext* browser_context)
: task_runner_(CreateSchedulerTaskRunner()),
observers_(base::MakeRefCounted<ObserverList>()) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
CacheStorageContextImpl::~CacheStorageContextImpl() {
// Can be destroyed on any thread.
task_runner_->ReleaseSoon(FROM_HERE, std::move(cache_manager_));
}
void CacheStorageContextImpl::Init(
const base::FilePath& user_data_directory,
scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
is_incognito_ = user_data_directory.empty();
special_storage_policy_ = std::move(special_storage_policy);
scoped_refptr<base::SequencedTaskRunner> cache_task_runner =
base::CreateSequencedTaskRunner(
{base::ThreadPool(), base::MayBlock(),
base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&CacheStorageContextImpl::CreateCacheStorageManagerOnTaskRunner, this,
user_data_directory, std::move(cache_task_runner),
quota_manager_proxy));
// If our target sequence is the IO thread, then the manager is guaranteed to
// be created before this task fires to create the quota clients. If we are
// running with a different target sequence then the quota client code will
// get a cross-sequence wrapper that is guaranteed to initialize its internal
// SequenceBound<> object after the real manager is created.
base::PostTask(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&CacheStorageContextImpl::CreateQuotaClientsOnIOThread,
base::WrapRefCounted(this),
std::move(quota_manager_proxy)));
}
void CacheStorageContextImpl::Shutdown() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&CacheStorageContextImpl::ShutdownOnTaskRunner, this));
}
void CacheStorageContextImpl::AddReceiver(
mojo::PendingReceiver<blink::mojom::CacheStorage> receiver,
const url::Origin& origin) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!dispatcher_host_) {
dispatcher_host_ =
base::SequenceBound<CacheStorageDispatcherHost>(task_runner_);
dispatcher_host_.Post(FROM_HERE, &CacheStorageDispatcherHost::Init,
base::RetainedRef(this));
}
dispatcher_host_.Post(FROM_HERE, &CacheStorageDispatcherHost::AddReceiver,
std::move(receiver), origin);
}
scoped_refptr<CacheStorageManager> CacheStorageContextImpl::CacheManager() {
// If we're already on the target sequence, then just return the real manager.
//
// Note, we can't check for nullptr cache_manager_ here because it is not
// threadsafe. In addition we may be creating a cross-sequence manager
// wrapper while the task to set cache_manager_ is waiting to run. This
// should be fine since the cross-sequence wrapper will initialize after the
// manager is set. See the comment in Init().
if (task_runner_->RunsTasksInCurrentSequence())
return cache_manager_;
// Otherwise we have to create a cross-sequence wrapper to provide safe
// access.
return base::MakeRefCounted<CrossSequenceCacheStorageManager>(task_runner_,
this);
}
void CacheStorageContextImpl::SetBlobParametersForCache(
ChromeBlobStorageContext* blob_storage_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!blob_storage_context)
return;
// We can only get a WeakPtr to the BlobStorageContext on the IO thread.
// Bounce there first before setting the context on the manager.
base::PostTask(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(
&CacheStorageContextImpl::GetBlobStorageContextWeakPtrOnIOThread,
this, base::RetainedRef(blob_storage_context)));
}
void CacheStorageContextImpl::GetAllOriginsInfo(
CacheStorageContext::GetUsageInfoCallback callback) {
// Can be called on any sequence.
callback = base::BindOnce(
[](scoped_refptr<base::SequencedTaskRunner> reply_task_runner,
GetUsageInfoCallback inner,
const std::vector<StorageUsageInfo>& entries) {
reply_task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(inner), entries));
},
base::SequencedTaskRunnerHandle::Get(), std::move(callback));
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<CacheStorageContextImpl> context,
GetUsageInfoCallback callback) {
if (!context->CacheManager()) {
std::move(callback).Run(std::vector<StorageUsageInfo>());
return;
}
context->CacheManager()->GetAllOriginsUsage(
CacheStorageOwner::kCacheAPI, std::move(callback));
},
base::RetainedRef(this), std::move(callback)));
}
void CacheStorageContextImpl::DeleteForOrigin(const GURL& origin) {
// Can be called on any sequence.
task_runner_->PostTask(FROM_HERE,
base::BindOnce(
[](scoped_refptr<CacheStorageContextImpl> context,
const GURL& origin) {
if (!context->CacheManager())
return;
context->CacheManager()->DeleteOriginData(
url::Origin::Create(origin),
CacheStorageOwner::kCacheAPI);
},
base::RetainedRef(this), origin));
}
void CacheStorageContextImpl::AddObserver(
CacheStorageContextImpl::Observer* observer) {
// Any sequence
observers_->AddObserver(observer);
}
void CacheStorageContextImpl::RemoveObserver(
CacheStorageContextImpl::Observer* observer) {
// Any sequence
observers_->RemoveObserver(observer);
}
void CacheStorageContextImpl::CreateCacheStorageManagerOnTaskRunner(
const base::FilePath& user_data_directory,
scoped_refptr<base::SequencedTaskRunner> cache_task_runner,
scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK(!cache_manager_);
cache_manager_ = LegacyCacheStorageManager::Create(
user_data_directory, std::move(cache_task_runner), task_runner_,
quota_manager_proxy, observers_);
}
void CacheStorageContextImpl::ShutdownOnTaskRunner() {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// Delete session-only ("clear on exit") origins.
if (special_storage_policy_ &&
special_storage_policy_->HasSessionOnlyOrigins()) {
cache_manager_->GetAllOriginsUsage(
CacheStorageOwner::kCacheAPI,
// TODO(jsbell): Make this BindOnce.
base::BindRepeating(
[](scoped_refptr<CacheStorageManager> cache_manager,
scoped_refptr<storage::SpecialStoragePolicy>
special_storage_policy,
const std::vector<StorageUsageInfo>& usage_info) {
for (const auto& info : usage_info) {
if (special_storage_policy->IsStorageSessionOnly(
info.origin.GetURL()) &&
!special_storage_policy->IsStorageProtected(
info.origin.GetURL())) {
cache_manager->DeleteOriginData(
info.origin, CacheStorageOwner::kCacheAPI,
// Retain a reference to the manager until the deletion is
// complete, since it internally uses weak pointers for
// the various stages of deletion and nothing else will
// keep it alive during shutdown.
base::BindOnce(
[](scoped_refptr<CacheStorageManager> cache_manager,
blink::mojom::QuotaStatusCode) {},
cache_manager));
}
}
},
cache_manager_, special_storage_policy_));
}
// Release |cache_manager_|. New clients will get a nullptr if they request
// an instance of CacheStorageManager after this. Any other client that
// ref-wrapped |cache_manager_| will be able to continue using it, and the
// CacheStorageManager will be destroyed when all the references are
// destroyed.
cache_manager_ = nullptr;
}
void CacheStorageContextImpl::GetBlobStorageContextWeakPtrOnIOThread(
ChromeBlobStorageContext* blob_storage_context) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(blob_storage_context);
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner, this,
blob_storage_context->context()->AsWeakPtr()));
}
void CacheStorageContextImpl::SetBlobParametersForCacheOnTaskRunner(
base::WeakPtr<storage::BlobStorageContext> blob_storage_context) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (cache_manager_)
cache_manager_->SetBlobParametersForCache(blob_storage_context);
}
void CacheStorageContextImpl::CreateQuotaClientsOnIOThread(
scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!quota_manager_proxy.get())
return;
quota_manager_proxy->RegisterClient(new CacheStorageQuotaClient(
CacheManager(), CacheStorageOwner::kCacheAPI));
quota_manager_proxy->RegisterClient(new CacheStorageQuotaClient(
CacheManager(), CacheStorageOwner::kBackgroundFetch));
}
} // namespace content