blob: e207e1c510dd5aa26c99681f39eb30ab8369f8f5 [file] [log] [blame]
// Copyright (c) 2012 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/appcache/appcache_quota_client.h"
#include <algorithm>
#include <map>
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "content/browser/appcache/appcache_service_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "storage/browser/quota/quota_client_type.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
using blink::mojom::StorageType;
namespace content {
namespace {
blink::mojom::QuotaStatusCode NetErrorCodeToQuotaStatus(int code) {
if (code == net::OK)
return blink::mojom::QuotaStatusCode::kOk;
else if (code == net::ERR_ABORTED)
return blink::mojom::QuotaStatusCode::kErrorAbort;
else
return blink::mojom::QuotaStatusCode::kUnknown;
}
void RunFront(content::AppCacheQuotaClient::RequestQueue* queue) {
base::OnceClosure request = std::move(queue->front());
queue->pop_front();
std::move(request).Run();
}
void RunDeleteOnIO(const base::Location& from_here,
net::CompletionRepeatingCallback callback,
int result) {
GetIOThreadTaskRunner({})->PostTask(
from_here, base::BindOnce(std::move(callback), result));
}
} // namespace
AppCacheQuotaClient::AppCacheQuotaClient(
base::WeakPtr<AppCacheServiceImpl> service)
: service_(std::move(service)) {}
AppCacheQuotaClient::~AppCacheQuotaClient() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(pending_batch_requests_.empty());
DCHECK(pending_serial_requests_.empty());
DCHECK(current_delete_request_callback_.is_null());
}
void AppCacheQuotaClient::OnMojoDisconnect() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DeletePendingRequests();
if (!current_delete_request_callback_.is_null()) {
current_delete_request_callback_.Reset();
GetServiceDeleteCallback()->Cancel();
}
}
void AppCacheQuotaClient::GetOriginUsage(const url::Origin& origin,
StorageType type,
GetOriginUsageCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
DCHECK(!callback.is_null());
if (service_is_destroyed_) {
std::move(callback).Run(0);
return;
}
if (!appcache_is_ready_) {
// base::Unretained usage is safe here because the callbacks are stored
// in a collection owned by this object.
pending_batch_requests_.push_back(base::BindOnce(
&AppCacheQuotaClient::GetOriginUsage, base::Unretained(this), origin,
type, std::move(callback)));
return;
}
GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](base::WeakPtr<AppCacheServiceImpl> service,
const url::Origin& origin) -> int64_t {
if (!service)
return 0;
const std::map<url::Origin, int64_t>& map =
service->storage()->usage_map();
auto it = map.find(origin);
if (it == map.end())
return 0;
return it->second;
},
service_, origin),
std::move(callback));
}
void AppCacheQuotaClient::GetOriginsForType(
StorageType type,
GetOriginsForTypeCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
DCHECK(!callback.is_null());
GetOriginsHelper(std::string(), std::move(callback));
}
void AppCacheQuotaClient::GetOriginsForHost(
StorageType type,
const std::string& host,
GetOriginsForHostCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
DCHECK(!callback.is_null());
if (host.empty()) {
std::move(callback).Run(std::vector<url::Origin>());
return;
}
GetOriginsHelper(host, std::move(callback));
}
void AppCacheQuotaClient::DeleteOriginData(const url::Origin& origin,
StorageType type,
DeleteOriginDataCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
DCHECK(!callback.is_null());
if (service_is_destroyed_) {
std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorAbort);
return;
}
if (!appcache_is_ready_ || !current_delete_request_callback_.is_null()) {
// base::Unretained usage is safe here because the callbacks are stored
// in a collection owned by this object.
pending_serial_requests_.push_back(base::BindOnce(
&AppCacheQuotaClient::DeleteOriginData, base::Unretained(this), origin,
type, std::move(callback)));
return;
}
current_delete_request_callback_ = std::move(callback);
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&AppCacheServiceImpl::DeleteAppCachesForOrigin, service_,
origin,
base::BindOnce(&RunDeleteOnIO, FROM_HERE,
GetServiceDeleteCallback()->callback())));
}
void AppCacheQuotaClient::PerformStorageCleanup(
blink::mojom::StorageType type,
PerformStorageCleanupCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(type, blink::mojom::StorageType::kTemporary);
DCHECK(!callback.is_null());
std::move(callback).Run();
}
void AppCacheQuotaClient::DidDeleteAppCachesForOrigin(int rv) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Finish the request by calling our callers callback.
std::move(current_delete_request_callback_)
.Run(NetErrorCodeToQuotaStatus(rv));
if (pending_serial_requests_.empty())
return;
// Start the next in the queue.
RunFront(&pending_serial_requests_);
}
void AppCacheQuotaClient::GetOriginsHelper(const std::string& opt_host,
GetOriginsForTypeCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!callback.is_null());
if (service_is_destroyed_) {
std::move(callback).Run(std::vector<url::Origin>());
return;
}
if (!appcache_is_ready_) {
// base::Unretained usage is safe here because the callbacks are stored
// in a collection owned by this object.
pending_batch_requests_.push_back(
base::BindOnce(&AppCacheQuotaClient::GetOriginsHelper,
base::Unretained(this), opt_host, std::move(callback)));
return;
}
GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](base::WeakPtr<AppCacheServiceImpl> service,
const std::string& opt_host) {
std::vector<url::Origin> origins;
if (!service)
return origins;
for (const auto& pair : service->storage()->usage_map()) {
if (opt_host.empty() || pair.first.host() == opt_host)
origins.push_back(pair.first);
}
return origins;
},
service_, opt_host),
std::move(callback));
}
void AppCacheQuotaClient::ProcessPendingRequests() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(appcache_is_ready_);
while (!pending_batch_requests_.empty())
RunFront(&pending_batch_requests_);
if (!pending_serial_requests_.empty())
RunFront(&pending_serial_requests_);
}
void AppCacheQuotaClient::DeletePendingRequests() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pending_batch_requests_.clear();
pending_serial_requests_.clear();
}
net::CancelableCompletionRepeatingCallback*
AppCacheQuotaClient::GetServiceDeleteCallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Lazily created due to base::CancelableRepeatingCallback's threading
// restrictions, there is no way to detach from the thread created on.
if (!service_delete_callback_) {
// base::Unretained usage is safe here because the callback is stored in a
// member of this object.
service_delete_callback_ =
std::make_unique<net::CancelableCompletionRepeatingCallback>(
base::BindRepeating(
&AppCacheQuotaClient::DidDeleteAppCachesForOrigin,
base::Unretained(this)));
}
return service_delete_callback_.get();
}
void AppCacheQuotaClient::NotifyStorageReady() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Can reoccur during reinitialization.
if (!appcache_is_ready_) {
appcache_is_ready_ = true;
ProcessPendingRequests();
}
}
void AppCacheQuotaClient::NotifyServiceDestroyed() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
service_ = nullptr;
service_is_destroyed_ = true;
while (!pending_batch_requests_.empty())
RunFront(&pending_batch_requests_);
while (!pending_serial_requests_.empty())
RunFront(&pending_serial_requests_);
if (!current_delete_request_callback_.is_null()) {
std::move(current_delete_request_callback_)
.Run(blink::mojom::QuotaStatusCode::kErrorAbort);
GetServiceDeleteCallback()->Cancel();
}
if (service_delete_callback_)
service_delete_callback_.reset();
}
} // namespace content