| // 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/bind_helpers.h" |
| #include "content/browser/appcache/appcache_service_impl.h" |
| #include "third_party/blink/public/mojom/quota/quota_types.mojom.h" |
| |
| using blink::mojom::StorageType; |
| using storage::QuotaClient; |
| |
| 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(); |
| } |
| } // namespace |
| |
| namespace content { |
| |
| AppCacheQuotaClient::AppCacheQuotaClient(AppCacheServiceImpl* service) |
| : service_(service), |
| appcache_is_ready_(false), |
| quota_manager_is_destroyed_(false) { |
| } |
| |
| AppCacheQuotaClient::~AppCacheQuotaClient() { |
| DCHECK(pending_batch_requests_.empty()); |
| DCHECK(pending_serial_requests_.empty()); |
| DCHECK(current_delete_request_callback_.is_null()); |
| } |
| |
| QuotaClient::ID AppCacheQuotaClient::id() const { |
| return kAppcache; |
| } |
| |
| void AppCacheQuotaClient::OnQuotaManagerDestroyed() { |
| DeletePendingRequests(); |
| if (!current_delete_request_callback_.is_null()) { |
| current_delete_request_callback_.Reset(); |
| GetServiceDeleteCallback()->Cancel(); |
| } |
| |
| quota_manager_is_destroyed_ = true; |
| if (!service_) |
| delete this; |
| } |
| |
| void AppCacheQuotaClient::GetOriginUsage(const url::Origin& origin, |
| StorageType type, |
| GetUsageCallback callback) { |
| DCHECK(!callback.is_null()); |
| DCHECK(!quota_manager_is_destroyed_); |
| |
| if (!service_) { |
| std::move(callback).Run(0); |
| return; |
| } |
| |
| if (!appcache_is_ready_) { |
| pending_batch_requests_.push_back(base::BindOnce( |
| &AppCacheQuotaClient::GetOriginUsage, base::Unretained(this), origin, |
| type, std::move(callback))); |
| return; |
| } |
| |
| if (type != StorageType::kTemporary) { |
| std::move(callback).Run(0); |
| return; |
| } |
| |
| const AppCacheStorage::UsageMap* map = GetUsageMap(); |
| auto found = map->find(origin); |
| if (found == map->end()) { |
| std::move(callback).Run(0); |
| return; |
| } |
| std::move(callback).Run(found->second); |
| } |
| |
| void AppCacheQuotaClient::GetOriginsForType(StorageType type, |
| GetOriginsCallback callback) { |
| GetOriginsHelper(type, std::string(), std::move(callback)); |
| } |
| |
| void AppCacheQuotaClient::GetOriginsForHost(StorageType type, |
| const std::string& host, |
| GetOriginsCallback callback) { |
| DCHECK(!callback.is_null()); |
| if (host.empty()) { |
| std::move(callback).Run(std::set<url::Origin>()); |
| return; |
| } |
| GetOriginsHelper(type, host, std::move(callback)); |
| } |
| |
| void AppCacheQuotaClient::DeleteOriginData(const url::Origin& origin, |
| StorageType type, |
| DeletionCallback callback) { |
| DCHECK(!quota_manager_is_destroyed_); |
| |
| if (!service_) { |
| std::move(callback).Run(blink::mojom::QuotaStatusCode::kErrorAbort); |
| return; |
| } |
| |
| if (!appcache_is_ready_ || !current_delete_request_callback_.is_null()) { |
| 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); |
| if (type != StorageType::kTemporary) { |
| DidDeleteAppCachesForOrigin(net::OK); |
| return; |
| } |
| |
| service_->DeleteAppCachesForOrigin(origin, |
| GetServiceDeleteCallback()->callback()); |
| } |
| |
| bool AppCacheQuotaClient::DoesSupport(StorageType type) const { |
| return type == StorageType::kTemporary; |
| } |
| |
| void AppCacheQuotaClient::DidDeleteAppCachesForOrigin(int rv) { |
| DCHECK(service_); |
| if (quota_manager_is_destroyed_) |
| return; |
| |
| // 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(StorageType type, |
| const std::string& opt_host, |
| GetOriginsCallback callback) { |
| DCHECK(!callback.is_null()); |
| DCHECK(!quota_manager_is_destroyed_); |
| |
| if (!service_) { |
| std::move(callback).Run(std::set<url::Origin>()); |
| return; |
| } |
| |
| if (!appcache_is_ready_) { |
| pending_batch_requests_.push_back(base::BindOnce( |
| &AppCacheQuotaClient::GetOriginsHelper, base::Unretained(this), type, |
| opt_host, std::move(callback))); |
| return; |
| } |
| |
| if (type != StorageType::kTemporary) { |
| std::move(callback).Run(std::set<url::Origin>()); |
| return; |
| } |
| |
| std::set<url::Origin> origins; |
| for (const auto& pair : *GetUsageMap()) { |
| if (opt_host.empty() || pair.first.host() == opt_host) |
| origins.insert(pair.first); |
| } |
| std::move(callback).Run(origins); |
| } |
| |
| void AppCacheQuotaClient::ProcessPendingRequests() { |
| 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() { |
| pending_batch_requests_.clear(); |
| pending_serial_requests_.clear(); |
| } |
| |
| const AppCacheStorage::UsageMap* AppCacheQuotaClient::GetUsageMap() { |
| DCHECK(service_); |
| return service_->storage()->usage_map(); |
| } |
| |
| net::CancelableCompletionRepeatingCallback* |
| AppCacheQuotaClient::GetServiceDeleteCallback() { |
| // Lazily created due to base::CancelableCallback's threading restrictions, |
| // there is no way to detach from the thread created on. |
| if (!service_delete_callback_) { |
| service_delete_callback_ = |
| std::make_unique<net::CancelableCompletionRepeatingCallback>( |
| base::BindRepeating( |
| &AppCacheQuotaClient::DidDeleteAppCachesForOrigin, |
| base::Unretained(this))); |
| } |
| return service_delete_callback_.get(); |
| } |
| |
| void AppCacheQuotaClient::NotifyAppCacheReady() { |
| // Can reoccur during reinitialization. |
| if (!appcache_is_ready_) { |
| appcache_is_ready_ = true; |
| ProcessPendingRequests(); |
| } |
| } |
| |
| void AppCacheQuotaClient::NotifyAppCacheDestroyed() { |
| service_ = nullptr; |
| 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 (quota_manager_is_destroyed_) |
| delete this; |
| } |
| |
| } // namespace content |