1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/cache_storage/cache_storage_manager.h"
10 #include "base/bind.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/files/file_util.h"
13 #include "base/id_map.h"
14 #include "base/sha1.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "content/browser/cache_storage/cache_storage.h"
18 #include "content/browser/cache_storage/cache_storage.pb.h"
19 #include "content/browser/cache_storage/cache_storage_quota_client.h"
20 #include "content/browser/service_worker/service_worker_context_core.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "net/base/net_util.h"
23 #include "storage/browser/quota/quota_manager_proxy.h"
24 #include "storage/common/database/database_identifier.h"
25 #include "storage/common/quota/quota_status_code.h"
32 bool DeleteDir(const base::FilePath
& path
) {
33 return base::DeleteFile(path
, true /* recursive */);
36 void DeleteOriginDidDeleteDir(
37 const storage::QuotaClient::DeletionCallback
& callback
,
39 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
41 callback
.Run(rv
? storage::kQuotaStatusOk
: storage::kQuotaErrorAbort
);
44 std::set
<GURL
> ListOriginsOnDisk(base::FilePath root_path_
) {
45 std::set
<GURL
> origins
;
46 base::FileEnumerator
file_enum(root_path_
, false /* recursive */,
47 base::FileEnumerator::DIRECTORIES
);
50 while (!(path
= file_enum
.Next()).empty()) {
52 base::ReadFileToString(path
.AppendASCII(CacheStorage::kIndexFileName
),
55 CacheStorageIndex index
;
56 if (index
.ParseFromString(protobuf
)) {
57 if (index
.has_origin())
58 origins
.insert(GURL(index
.origin()));
65 void GetOriginsForHostDidListOrigins(
66 const std::string
& host
,
67 const storage::QuotaClient::GetOriginsCallback
& callback
,
68 const std::set
<GURL
>& origins
) {
69 std::set
<GURL
> out_origins
;
70 for (const GURL
& origin
: origins
) {
71 if (host
== net::GetHostOrSpecFromURL(origin
))
72 out_origins
.insert(origin
);
74 callback
.Run(out_origins
);
80 scoped_ptr
<CacheStorageManager
> CacheStorageManager::Create(
81 const base::FilePath
& path
,
82 const scoped_refptr
<base::SequencedTaskRunner
>& cache_task_runner
,
83 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
) {
84 base::FilePath root_path
= path
;
86 root_path
= path
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
87 .AppendASCII("CacheStorage");
90 return make_scoped_ptr(new CacheStorageManager(root_path
, cache_task_runner
,
91 quota_manager_proxy
));
95 scoped_ptr
<CacheStorageManager
> CacheStorageManager::Create(
96 CacheStorageManager
* old_manager
) {
97 scoped_ptr
<CacheStorageManager
> manager(new CacheStorageManager(
98 old_manager
->root_path(), old_manager
->cache_task_runner(),
99 old_manager
->quota_manager_proxy_
.get()));
100 // These values may be NULL, in which case this will be called again later by
101 // the dispatcher host per usual.
102 manager
->SetBlobParametersForCache(old_manager
->url_request_context(),
103 old_manager
->blob_storage_context());
104 return manager
.Pass();
107 CacheStorageManager::~CacheStorageManager() {
108 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
109 for (CacheStorageMap::iterator it
= cache_storage_map_
.begin();
110 it
!= cache_storage_map_
.end(); ++it
) {
115 void CacheStorageManager::OpenCache(
117 const std::string
& cache_name
,
118 const CacheStorage::CacheAndErrorCallback
& callback
) {
119 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
121 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
123 cache_storage
->OpenCache(cache_name
, callback
);
126 void CacheStorageManager::HasCache(
128 const std::string
& cache_name
,
129 const CacheStorage::BoolAndErrorCallback
& callback
) {
130 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
132 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
133 cache_storage
->HasCache(cache_name
, callback
);
136 void CacheStorageManager::DeleteCache(
138 const std::string
& cache_name
,
139 const CacheStorage::BoolAndErrorCallback
& callback
) {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
142 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
143 cache_storage
->DeleteCache(cache_name
, callback
);
146 void CacheStorageManager::EnumerateCaches(
148 const CacheStorage::StringsAndErrorCallback
& callback
) {
149 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
151 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
153 cache_storage
->EnumerateCaches(callback
);
156 void CacheStorageManager::MatchCache(
158 const std::string
& cache_name
,
159 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
160 const CacheStorageCache::ResponseCallback
& callback
) {
161 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
163 cache_storage
->MatchCache(cache_name
, request
.Pass(), callback
);
166 void CacheStorageManager::MatchAllCaches(
168 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
169 const CacheStorageCache::ResponseCallback
& callback
) {
170 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
172 cache_storage
->MatchAllCaches(request
.Pass(), callback
);
175 void CacheStorageManager::SetBlobParametersForCache(
176 net::URLRequestContext
* request_context
,
177 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
) {
178 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
179 DCHECK(cache_storage_map_
.empty());
180 DCHECK(!request_context_
|| request_context_
== request_context
);
181 DCHECK(!blob_context_
|| blob_context_
.get() == blob_storage_context
.get());
182 request_context_
= request_context
;
183 blob_context_
= blob_storage_context
;
186 void CacheStorageManager::GetOriginUsage(
187 const GURL
& origin_url
,
188 const storage::QuotaClient::GetUsageCallback
& callback
) {
189 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
191 if (IsMemoryBacked()) {
193 for (const auto& key_value
: cache_storage_map_
)
194 sum
+= key_value
.second
->MemoryBackedSize();
199 MigrateOrigin(origin_url
);
200 PostTaskAndReplyWithResult(
201 cache_task_runner_
.get(), FROM_HERE
,
202 base::Bind(base::ComputeDirectorySize
,
203 ConstructOriginPath(root_path_
, origin_url
)),
204 base::Bind(callback
));
207 void CacheStorageManager::GetOrigins(
208 const storage::QuotaClient::GetOriginsCallback
& callback
) {
209 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
211 if (IsMemoryBacked()) {
212 std::set
<GURL
> origins
;
213 for (const auto& key_value
: cache_storage_map_
)
214 origins
.insert(key_value
.first
);
216 callback
.Run(origins
);
220 PostTaskAndReplyWithResult(cache_task_runner_
.get(), FROM_HERE
,
221 base::Bind(&ListOriginsOnDisk
, root_path_
),
222 base::Bind(callback
));
225 void CacheStorageManager::GetOriginsForHost(
226 const std::string
& host
,
227 const storage::QuotaClient::GetOriginsCallback
& callback
) {
228 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
230 if (IsMemoryBacked()) {
231 std::set
<GURL
> origins
;
232 for (const auto& key_value
: cache_storage_map_
) {
233 if (host
== net::GetHostOrSpecFromURL(key_value
.first
))
234 origins
.insert(key_value
.first
);
236 callback
.Run(origins
);
240 PostTaskAndReplyWithResult(
241 cache_task_runner_
.get(), FROM_HERE
,
242 base::Bind(&ListOriginsOnDisk
, root_path_
),
243 base::Bind(&GetOriginsForHostDidListOrigins
, host
, callback
));
246 void CacheStorageManager::DeleteOriginData(
248 const storage::QuotaClient::DeletionCallback
& callback
) {
249 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
251 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
252 cache_storage_map_
.erase(origin
);
253 cache_storage
->CloseAllCaches(
254 base::Bind(&CacheStorageManager::DeleteOriginDidClose
, origin
, callback
,
255 base::Passed(make_scoped_ptr(cache_storage
)),
256 weak_ptr_factory_
.GetWeakPtr()));
260 void CacheStorageManager::DeleteOriginDidClose(
262 const storage::QuotaClient::DeletionCallback
& callback
,
263 scoped_ptr
<CacheStorage
> cache_storage
,
264 base::WeakPtr
<CacheStorageManager
> cache_manager
) {
265 // TODO(jkarlin): Deleting the storage leaves any unfinished operations
266 // hanging, resulting in unresolved promises. Fix this by guaranteeing that
267 // callbacks are called in ServiceWorkerStorage.
268 cache_storage
.reset();
270 if (!cache_manager
) {
271 callback
.Run(storage::kQuotaErrorAbort
);
275 if (cache_manager
->IsMemoryBacked()) {
276 callback
.Run(storage::kQuotaStatusOk
);
280 cache_manager
->MigrateOrigin(origin
);
281 PostTaskAndReplyWithResult(
282 cache_manager
->cache_task_runner_
.get(), FROM_HERE
,
283 base::Bind(&DeleteDir
,
284 ConstructOriginPath(cache_manager
->root_path_
, origin
)),
285 base::Bind(&DeleteOriginDidDeleteDir
, callback
));
288 CacheStorageManager::CacheStorageManager(
289 const base::FilePath
& path
,
290 const scoped_refptr
<base::SequencedTaskRunner
>& cache_task_runner
,
291 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
)
293 cache_task_runner_(cache_task_runner
),
294 quota_manager_proxy_(quota_manager_proxy
),
295 request_context_(NULL
),
296 weak_ptr_factory_(this) {
297 if (quota_manager_proxy_
.get()) {
298 quota_manager_proxy_
->RegisterClient(
299 new CacheStorageQuotaClient(weak_ptr_factory_
.GetWeakPtr()));
303 CacheStorage
* CacheStorageManager::FindOrCreateCacheStorage(
304 const GURL
& origin
) {
305 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
306 DCHECK(request_context_
);
307 CacheStorageMap::const_iterator it
= cache_storage_map_
.find(origin
);
308 if (it
== cache_storage_map_
.end()) {
309 MigrateOrigin(origin
);
310 CacheStorage
* cache_storage
= new CacheStorage(
311 ConstructOriginPath(root_path_
, origin
), IsMemoryBacked(),
312 cache_task_runner_
.get(), request_context_
, quota_manager_proxy_
,
313 blob_context_
, origin
);
314 // The map owns fetch_stores.
315 cache_storage_map_
.insert(std::make_pair(origin
, cache_storage
));
316 return cache_storage
;
322 base::FilePath
CacheStorageManager::ConstructLegacyOriginPath(
323 const base::FilePath
& root_path
,
324 const GURL
& origin
) {
325 const std::string origin_hash
= base::SHA1HashString(origin
.spec());
326 const std::string origin_hash_hex
= base::StringToLowerASCII(
327 base::HexEncode(origin_hash
.c_str(), origin_hash
.length()));
328 return root_path
.AppendASCII(origin_hash_hex
);
332 base::FilePath
CacheStorageManager::ConstructOriginPath(
333 const base::FilePath
& root_path
,
334 const GURL
& origin
) {
335 const std::string identifier
= storage::GetIdentifierFromOrigin(origin
);
336 const std::string origin_hash
= base::SHA1HashString(identifier
);
337 const std::string origin_hash_hex
= base::StringToLowerASCII(
338 base::HexEncode(origin_hash
.c_str(), origin_hash
.length()));
339 return root_path
.AppendASCII(origin_hash_hex
);
342 // Migrate from old origin-based path to storage identifier-based path.
343 // TODO(jsbell); Remove after a few releases.
344 void CacheStorageManager::MigrateOrigin(const GURL
& origin
) {
345 if (IsMemoryBacked())
347 base::FilePath old_path
= ConstructLegacyOriginPath(root_path_
, origin
);
348 base::FilePath new_path
= ConstructOriginPath(root_path_
, origin
);
349 cache_task_runner_
->PostTask(
350 FROM_HERE
, base::Bind(&MigrateOriginOnTaskRunner
, old_path
, new_path
));
354 void CacheStorageManager::MigrateOriginOnTaskRunner(
355 const base::FilePath
& old_path
,
356 const base::FilePath
& new_path
) {
357 if (base::PathExists(old_path
)) {
358 if (!base::PathExists(new_path
))
359 base::Move(old_path
, new_path
);
360 base::DeleteFile(old_path
, /*recursive*/ true);
364 } // namespace content