// 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 "webkit/browser/appcache/appcache_quota_client.h" #include <algorithm> #include <map> #include <set> #include "base/bind.h" #include "base/bind_helpers.h" #include "webkit/browser/appcache/appcache_service.h" using quota::QuotaClient; namespace { quota::QuotaStatusCode NetErrorCodeToQuotaStatus(int code) { if (code == net::OK) return quota::kQuotaStatusOk; else if (code == net::ERR_ABORTED) return quota::kQuotaErrorAbort; else return quota::kQuotaStatusUnknown; } void RunFront(appcache::AppCacheQuotaClient::RequestQueue* queue) { base::Closure request = queue->front(); queue->pop_front(); request.Run(); } } // namespace namespace appcache { AppCacheQuotaClient::AppCacheQuotaClient(AppCacheService* 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 GURL& origin, quota::StorageType type, const GetUsageCallback& callback) { DCHECK(!callback.is_null()); DCHECK(!quota_manager_is_destroyed_); if (!service_) { callback.Run(0); return; } if (!appcache_is_ready_) { pending_batch_requests_.push_back( base::Bind(&AppCacheQuotaClient::GetOriginUsage, base::Unretained(this), origin, type, callback)); return; } if (type != quota::kStorageTypeTemporary) { callback.Run(0); return; } const AppCacheStorage::UsageMap* map = GetUsageMap(); AppCacheStorage::UsageMap::const_iterator found = map->find(origin); if (found == map->end()) { callback.Run(0); return; } callback.Run(found->second); } void AppCacheQuotaClient::GetOriginsForType( quota::StorageType type, const GetOriginsCallback& callback) { GetOriginsHelper(type, std::string(), callback); } void AppCacheQuotaClient::GetOriginsForHost( quota::StorageType type, const std::string& host, const GetOriginsCallback& callback) { DCHECK(!callback.is_null()); if (host.empty()) { callback.Run(std::set<GURL>()); return; } GetOriginsHelper(type, host, callback); } void AppCacheQuotaClient::DeleteOriginData(const GURL& origin, quota::StorageType type, const DeletionCallback& callback) { DCHECK(!quota_manager_is_destroyed_); if (!service_) { callback.Run(quota::kQuotaErrorAbort); return; } if (!appcache_is_ready_ || !current_delete_request_callback_.is_null()) { pending_serial_requests_.push_back( base::Bind(&AppCacheQuotaClient::DeleteOriginData, base::Unretained(this), origin, type, callback)); return; } current_delete_request_callback_ = callback; if (type != quota::kStorageTypeTemporary) { DidDeleteAppCachesForOrigin(net::OK); return; } service_->DeleteAppCachesForOrigin( origin, GetServiceDeleteCallback()->callback()); } bool AppCacheQuotaClient::DoesSupport(quota::StorageType type) const { return type == quota::kStorageTypeTemporary; } void AppCacheQuotaClient::DidDeleteAppCachesForOrigin(int rv) { DCHECK(service_); if (quota_manager_is_destroyed_) return; // Finish the request by calling our callers callback. current_delete_request_callback_.Run(NetErrorCodeToQuotaStatus(rv)); current_delete_request_callback_.Reset(); if (pending_serial_requests_.empty()) return; // Start the next in the queue. RunFront(&pending_serial_requests_); } void AppCacheQuotaClient::GetOriginsHelper( quota::StorageType type, const std::string& opt_host, const GetOriginsCallback& callback) { DCHECK(!callback.is_null()); DCHECK(!quota_manager_is_destroyed_); if (!service_) { callback.Run(std::set<GURL>()); return; } if (!appcache_is_ready_) { pending_batch_requests_.push_back( base::Bind(&AppCacheQuotaClient::GetOriginsHelper, base::Unretained(this), type, opt_host, callback)); return; } if (type != quota::kStorageTypeTemporary) { callback.Run(std::set<GURL>()); return; } const AppCacheStorage::UsageMap* map = GetUsageMap(); std::set<GURL> origins; for (AppCacheStorage::UsageMap::const_iterator iter = map->begin(); iter != map->end(); ++iter) { if (opt_host.empty() || iter->first.host() == opt_host) origins.insert(iter->first); } 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::CancelableCompletionCallback* AppCacheQuotaClient::GetServiceDeleteCallback() { // Lazily created due to CancelableCompletionCallback's threading // restrictions, there is no way to detach from the thread created on. if (!service_delete_callback_) { service_delete_callback_.reset( new net::CancelableCompletionCallback( base::Bind(&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_ = NULL; 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()) { current_delete_request_callback_.Run(quota::kQuotaErrorAbort); current_delete_request_callback_.Reset(); GetServiceDeleteCallback()->Cancel(); } if (quota_manager_is_destroyed_) delete this; } } // namespace appcache