// Copyright 2014 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_manager.h" #include #include #include "base/bind.h" #include "base/files/file_enumerator.h" #include "base/files/file_util.h" #include "base/id_map.h" #include "base/sha1.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "content/browser/cache_storage/cache_storage.h" #include "content/browser/cache_storage/cache_storage.pb.h" #include "content/browser/cache_storage/cache_storage_quota_client.h" #include "content/browser/service_worker/service_worker_context_core.h" #include "content/public/browser/browser_thread.h" #include "net/base/net_util.h" #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/common/database/database_identifier.h" #include "storage/common/quota/quota_status_code.h" #include "url/gurl.h" namespace content { namespace { bool DeleteDir(const base::FilePath& path) { return base::DeleteFile(path, true /* recursive */); } void DeleteOriginDidDeleteDir( const storage::QuotaClient::DeletionCallback& callback, bool rv) { DCHECK_CURRENTLY_ON(BrowserThread::IO); callback.Run(rv ? storage::kQuotaStatusOk : storage::kQuotaErrorAbort); } std::set ListOriginsOnDisk(base::FilePath root_path_) { std::set origins; base::FileEnumerator file_enum(root_path_, false /* recursive */, base::FileEnumerator::DIRECTORIES); base::FilePath path; while (!(path = file_enum.Next()).empty()) { std::string protobuf; base::ReadFileToString(path.AppendASCII(CacheStorage::kIndexFileName), &protobuf); CacheStorageIndex index; if (index.ParseFromString(protobuf)) { if (index.has_origin()) origins.insert(GURL(index.origin())); } } return origins; } void GetOriginsForHostDidListOrigins( const std::string& host, const storage::QuotaClient::GetOriginsCallback& callback, const std::set& origins) { std::set out_origins; for (const GURL& origin : origins) { if (host == net::GetHostOrSpecFromURL(origin)) out_origins.insert(origin); } callback.Run(out_origins); } } // namespace // static scoped_ptr CacheStorageManager::Create( const base::FilePath& path, const scoped_refptr& cache_task_runner, const scoped_refptr& quota_manager_proxy) { base::FilePath root_path = path; if (!path.empty()) { root_path = path.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) .AppendASCII("CacheStorage"); } return make_scoped_ptr(new CacheStorageManager(root_path, cache_task_runner, quota_manager_proxy)); } // static scoped_ptr CacheStorageManager::Create( CacheStorageManager* old_manager) { scoped_ptr manager(new CacheStorageManager( old_manager->root_path(), old_manager->cache_task_runner(), old_manager->quota_manager_proxy_.get())); // These values may be NULL, in which case this will be called again later by // the dispatcher host per usual. manager->SetBlobParametersForCache(old_manager->url_request_context_getter(), old_manager->blob_storage_context()); return manager.Pass(); } CacheStorageManager::~CacheStorageManager() { DCHECK_CURRENTLY_ON(BrowserThread::IO); for (CacheStorageMap::iterator it = cache_storage_map_.begin(); it != cache_storage_map_.end(); ++it) { delete it->second; } } void CacheStorageManager::OpenCache( const GURL& origin, const std::string& cache_name, const CacheStorage::CacheAndErrorCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); cache_storage->OpenCache(cache_name, callback); } void CacheStorageManager::HasCache( const GURL& origin, const std::string& cache_name, const CacheStorage::BoolAndErrorCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); cache_storage->HasCache(cache_name, callback); } void CacheStorageManager::DeleteCache( const GURL& origin, const std::string& cache_name, const CacheStorage::BoolAndErrorCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); cache_storage->DeleteCache(cache_name, callback); } void CacheStorageManager::EnumerateCaches( const GURL& origin, const CacheStorage::StringsAndErrorCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); cache_storage->EnumerateCaches(callback); } void CacheStorageManager::MatchCache( const GURL& origin, const std::string& cache_name, scoped_ptr request, const CacheStorageCache::ResponseCallback& callback) { CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); cache_storage->MatchCache(cache_name, request.Pass(), callback); } void CacheStorageManager::MatchAllCaches( const GURL& origin, scoped_ptr request, const CacheStorageCache::ResponseCallback& callback) { CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); cache_storage->MatchAllCaches(request.Pass(), callback); } void CacheStorageManager::SetBlobParametersForCache( const scoped_refptr& request_context_getter, base::WeakPtr blob_storage_context) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(cache_storage_map_.empty()); DCHECK(!request_context_getter_ || request_context_getter_.get() == request_context_getter.get()); DCHECK(!blob_context_ || blob_context_.get() == blob_storage_context.get()); request_context_getter_ = request_context_getter; blob_context_ = blob_storage_context; } void CacheStorageManager::GetOriginUsage( const GURL& origin_url, const storage::QuotaClient::GetUsageCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (IsMemoryBacked()) { int64 sum = 0; for (const auto& key_value : cache_storage_map_) sum += key_value.second->MemoryBackedSize(); callback.Run(sum); return; } MigrateOrigin(origin_url); PostTaskAndReplyWithResult( cache_task_runner_.get(), FROM_HERE, base::Bind(base::ComputeDirectorySize, ConstructOriginPath(root_path_, origin_url)), base::Bind(callback)); } void CacheStorageManager::GetOrigins( const storage::QuotaClient::GetOriginsCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (IsMemoryBacked()) { std::set origins; for (const auto& key_value : cache_storage_map_) origins.insert(key_value.first); callback.Run(origins); return; } PostTaskAndReplyWithResult(cache_task_runner_.get(), FROM_HERE, base::Bind(&ListOriginsOnDisk, root_path_), base::Bind(callback)); } void CacheStorageManager::GetOriginsForHost( const std::string& host, const storage::QuotaClient::GetOriginsCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (IsMemoryBacked()) { std::set origins; for (const auto& key_value : cache_storage_map_) { if (host == net::GetHostOrSpecFromURL(key_value.first)) origins.insert(key_value.first); } callback.Run(origins); return; } PostTaskAndReplyWithResult( cache_task_runner_.get(), FROM_HERE, base::Bind(&ListOriginsOnDisk, root_path_), base::Bind(&GetOriginsForHostDidListOrigins, host, callback)); } void CacheStorageManager::DeleteOriginData( const GURL& origin, const storage::QuotaClient::DeletionCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); CacheStorage* cache_storage = FindOrCreateCacheStorage(origin); cache_storage_map_.erase(origin); cache_storage->CloseAllCaches( base::Bind(&CacheStorageManager::DeleteOriginDidClose, origin, callback, base::Passed(make_scoped_ptr(cache_storage)), weak_ptr_factory_.GetWeakPtr())); } // static void CacheStorageManager::DeleteOriginDidClose( const GURL& origin, const storage::QuotaClient::DeletionCallback& callback, scoped_ptr cache_storage, base::WeakPtr cache_manager) { // TODO(jkarlin): Deleting the storage leaves any unfinished operations // hanging, resulting in unresolved promises. Fix this by guaranteeing that // callbacks are called in ServiceWorkerStorage. cache_storage.reset(); if (!cache_manager) { callback.Run(storage::kQuotaErrorAbort); return; } if (cache_manager->IsMemoryBacked()) { callback.Run(storage::kQuotaStatusOk); return; } cache_manager->MigrateOrigin(origin); PostTaskAndReplyWithResult( cache_manager->cache_task_runner_.get(), FROM_HERE, base::Bind(&DeleteDir, ConstructOriginPath(cache_manager->root_path_, origin)), base::Bind(&DeleteOriginDidDeleteDir, callback)); } CacheStorageManager::CacheStorageManager( const base::FilePath& path, const scoped_refptr& cache_task_runner, const scoped_refptr& quota_manager_proxy) : root_path_(path), cache_task_runner_(cache_task_runner), quota_manager_proxy_(quota_manager_proxy), weak_ptr_factory_(this) { if (quota_manager_proxy_.get()) { quota_manager_proxy_->RegisterClient( new CacheStorageQuotaClient(weak_ptr_factory_.GetWeakPtr())); } } CacheStorage* CacheStorageManager::FindOrCreateCacheStorage( const GURL& origin) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(request_context_getter_); CacheStorageMap::const_iterator it = cache_storage_map_.find(origin); if (it == cache_storage_map_.end()) { MigrateOrigin(origin); CacheStorage* cache_storage = new CacheStorage( ConstructOriginPath(root_path_, origin), IsMemoryBacked(), cache_task_runner_.get(), request_context_getter_, quota_manager_proxy_, blob_context_, origin); // The map owns fetch_stores. cache_storage_map_.insert(std::make_pair(origin, cache_storage)); return cache_storage; } return it->second; } // static base::FilePath CacheStorageManager::ConstructLegacyOriginPath( const base::FilePath& root_path, const GURL& origin) { const std::string origin_hash = base::SHA1HashString(origin.spec()); const std::string origin_hash_hex = base::ToLowerASCII( base::HexEncode(origin_hash.c_str(), origin_hash.length())); return root_path.AppendASCII(origin_hash_hex); } // static base::FilePath CacheStorageManager::ConstructOriginPath( const base::FilePath& root_path, const GURL& origin) { const std::string identifier = storage::GetIdentifierFromOrigin(origin); const std::string origin_hash = base::SHA1HashString(identifier); const std::string origin_hash_hex = base::ToLowerASCII( base::HexEncode(origin_hash.c_str(), origin_hash.length())); return root_path.AppendASCII(origin_hash_hex); } // Migrate from old origin-based path to storage identifier-based path. // TODO(jsbell); Remove after a few releases. void CacheStorageManager::MigrateOrigin(const GURL& origin) { if (IsMemoryBacked()) return; base::FilePath old_path = ConstructLegacyOriginPath(root_path_, origin); base::FilePath new_path = ConstructOriginPath(root_path_, origin); cache_task_runner_->PostTask( FROM_HERE, base::Bind(&MigrateOriginOnTaskRunner, old_path, new_path)); } // static void CacheStorageManager::MigrateOriginOnTaskRunner( const base::FilePath& old_path, const base::FilePath& new_path) { if (base::PathExists(old_path)) { if (!base::PathExists(new_path)) base::Move(old_path, new_path); base::DeleteFile(old_path, /*recursive*/ true); } } } // namespace content