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/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/time/time.h"
19 #include "content/browser/cache_storage/cache_storage.h"
20 #include "content/browser/cache_storage/cache_storage.pb.h"
21 #include "content/browser/cache_storage/cache_storage_quota_client.h"
22 #include "content/browser/service_worker/service_worker_context_core.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "net/base/net_util.h"
25 #include "storage/browser/quota/quota_manager_proxy.h"
26 #include "storage/common/database/database_identifier.h"
27 #include "storage/common/quota/quota_status_code.h"
34 bool DeleteDir(const base::FilePath
& path
) {
35 return base::DeleteFile(path
, true /* recursive */);
38 void DeleteOriginDidDeleteDir(
39 const storage::QuotaClient::DeletionCallback
& callback
,
41 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
43 callback
.Run(rv
? storage::kQuotaStatusOk
: storage::kQuotaErrorAbort
);
46 std::set
<GURL
> ListOriginsOnDisk(base::FilePath root_path
) {
47 std::set
<GURL
> origins
;
48 base::FileEnumerator
file_enum(root_path
, false /* recursive */,
49 base::FileEnumerator::DIRECTORIES
);
52 while (!(path
= file_enum
.Next()).empty()) {
54 base::ReadFileToString(path
.AppendASCII(CacheStorage::kIndexFileName
),
57 CacheStorageIndex index
;
58 if (index
.ParseFromString(protobuf
)) {
59 if (index
.has_origin())
60 origins
.insert(GURL(index
.origin()));
67 std::vector
<CacheStorageUsageInfo
> GetAllOriginsUsageOnTaskRunner(
68 const base::FilePath root_path
) {
69 std::vector
<CacheStorageUsageInfo
> entries
;
70 const std::set
<GURL
> origins
= ListOriginsOnDisk(root_path
);
71 entries
.reserve(origins
.size());
72 for (const GURL
& origin
: origins
) {
74 CacheStorageManager::ConstructOriginPath(root_path
, origin
);
75 int64 size
= base::ComputeDirectorySize(path
);
76 base::File::Info file_info
;
77 base::Time last_modified
;
78 if (base::GetFileInfo(path
, &file_info
))
79 last_modified
= file_info
.last_modified
;
80 entries
.push_back(CacheStorageUsageInfo(origin
, size
, last_modified
));
85 void GetOriginsForHostDidListOrigins(
86 const std::string
& host
,
87 const storage::QuotaClient::GetOriginsCallback
& callback
,
88 const std::set
<GURL
>& origins
) {
89 std::set
<GURL
> out_origins
;
90 for (const GURL
& origin
: origins
) {
91 if (host
== net::GetHostOrSpecFromURL(origin
))
92 out_origins
.insert(origin
);
94 callback
.Run(out_origins
);
97 void EmptyQuotaStatusCallback(storage::QuotaStatusCode code
) {}
102 scoped_ptr
<CacheStorageManager
> CacheStorageManager::Create(
103 const base::FilePath
& path
,
104 const scoped_refptr
<base::SequencedTaskRunner
>& cache_task_runner
,
105 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
) {
106 base::FilePath root_path
= path
;
108 root_path
= path
.Append(ServiceWorkerContextCore::kServiceWorkerDirectory
)
109 .AppendASCII("CacheStorage");
112 return make_scoped_ptr(new CacheStorageManager(root_path
, cache_task_runner
,
113 quota_manager_proxy
));
117 scoped_ptr
<CacheStorageManager
> CacheStorageManager::Create(
118 CacheStorageManager
* old_manager
) {
119 scoped_ptr
<CacheStorageManager
> manager(new CacheStorageManager(
120 old_manager
->root_path(), old_manager
->cache_task_runner(),
121 old_manager
->quota_manager_proxy_
.get()));
122 // These values may be NULL, in which case this will be called again later by
123 // the dispatcher host per usual.
124 manager
->SetBlobParametersForCache(old_manager
->url_request_context_getter(),
125 old_manager
->blob_storage_context());
126 return manager
.Pass();
129 CacheStorageManager::~CacheStorageManager() {
130 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
131 for (CacheStorageMap::iterator it
= cache_storage_map_
.begin();
132 it
!= cache_storage_map_
.end(); ++it
) {
137 void CacheStorageManager::OpenCache(
139 const std::string
& cache_name
,
140 const CacheStorage::CacheAndErrorCallback
& callback
) {
141 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
143 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
145 cache_storage
->OpenCache(cache_name
, callback
);
148 void CacheStorageManager::HasCache(
150 const std::string
& cache_name
,
151 const CacheStorage::BoolAndErrorCallback
& callback
) {
152 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
154 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
155 cache_storage
->HasCache(cache_name
, callback
);
158 void CacheStorageManager::DeleteCache(
160 const std::string
& cache_name
,
161 const CacheStorage::BoolAndErrorCallback
& callback
) {
162 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
164 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
165 cache_storage
->DeleteCache(cache_name
, callback
);
168 void CacheStorageManager::EnumerateCaches(
170 const CacheStorage::StringsAndErrorCallback
& callback
) {
171 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
173 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
175 cache_storage
->EnumerateCaches(callback
);
178 void CacheStorageManager::MatchCache(
180 const std::string
& cache_name
,
181 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
182 const CacheStorageCache::ResponseCallback
& callback
) {
183 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
185 cache_storage
->MatchCache(cache_name
, request
.Pass(), callback
);
188 void CacheStorageManager::MatchAllCaches(
190 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
191 const CacheStorageCache::ResponseCallback
& callback
) {
192 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
194 cache_storage
->MatchAllCaches(request
.Pass(), callback
);
197 void CacheStorageManager::SetBlobParametersForCache(
198 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
199 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
) {
200 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
201 DCHECK(cache_storage_map_
.empty());
202 DCHECK(!request_context_getter_
||
203 request_context_getter_
.get() == request_context_getter
.get());
204 DCHECK(!blob_context_
|| blob_context_
.get() == blob_storage_context
.get());
205 request_context_getter_
= request_context_getter
;
206 blob_context_
= blob_storage_context
;
209 void CacheStorageManager::GetAllOriginsUsage(
210 const CacheStorageContext::GetUsageInfoCallback
& callback
) {
211 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
213 if (IsMemoryBacked()) {
214 std::vector
<CacheStorageUsageInfo
> entries
;
215 entries
.reserve(cache_storage_map_
.size());
216 for (const auto& origin_details
: cache_storage_map_
) {
217 entries
.push_back(CacheStorageUsageInfo(
218 origin_details
.first
, origin_details
.second
->MemoryBackedSize(),
221 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
222 base::Bind(callback
, entries
));
226 PostTaskAndReplyWithResult(
227 cache_task_runner_
.get(), FROM_HERE
,
228 base::Bind(&GetAllOriginsUsageOnTaskRunner
, root_path_
),
229 base::Bind(callback
));
232 void CacheStorageManager::GetOriginUsage(
233 const GURL
& origin_url
,
234 const storage::QuotaClient::GetUsageCallback
& callback
) {
235 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
237 if (IsMemoryBacked()) {
239 if (ContainsKey(cache_storage_map_
, origin_url
))
240 usage
= cache_storage_map_
[origin_url
]->MemoryBackedSize();
245 MigrateOrigin(origin_url
);
246 PostTaskAndReplyWithResult(
247 cache_task_runner_
.get(), FROM_HERE
,
248 base::Bind(base::ComputeDirectorySize
,
249 ConstructOriginPath(root_path_
, origin_url
)),
250 base::Bind(callback
));
253 void CacheStorageManager::GetOrigins(
254 const storage::QuotaClient::GetOriginsCallback
& callback
) {
255 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
257 if (IsMemoryBacked()) {
258 std::set
<GURL
> origins
;
259 for (const auto& key_value
: cache_storage_map_
)
260 origins
.insert(key_value
.first
);
262 callback
.Run(origins
);
266 PostTaskAndReplyWithResult(cache_task_runner_
.get(), FROM_HERE
,
267 base::Bind(&ListOriginsOnDisk
, root_path_
),
268 base::Bind(callback
));
271 void CacheStorageManager::GetOriginsForHost(
272 const std::string
& host
,
273 const storage::QuotaClient::GetOriginsCallback
& callback
) {
274 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
276 if (IsMemoryBacked()) {
277 std::set
<GURL
> origins
;
278 for (const auto& key_value
: cache_storage_map_
) {
279 if (host
== net::GetHostOrSpecFromURL(key_value
.first
))
280 origins
.insert(key_value
.first
);
282 callback
.Run(origins
);
286 PostTaskAndReplyWithResult(
287 cache_task_runner_
.get(), FROM_HERE
,
288 base::Bind(&ListOriginsOnDisk
, root_path_
),
289 base::Bind(&GetOriginsForHostDidListOrigins
, host
, callback
));
292 void CacheStorageManager::DeleteOriginData(
294 const storage::QuotaClient::DeletionCallback
& callback
) {
295 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
297 CacheStorage
* cache_storage
= FindOrCreateCacheStorage(origin
);
298 cache_storage_map_
.erase(origin
);
299 cache_storage
->CloseAllCaches(
300 base::Bind(&CacheStorageManager::DeleteOriginDidClose
, origin
, callback
,
301 base::Passed(make_scoped_ptr(cache_storage
)),
302 weak_ptr_factory_
.GetWeakPtr()));
305 void CacheStorageManager::DeleteOriginData(const GURL
& origin
) {
306 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
307 DeleteOriginData(origin
, base::Bind(&EmptyQuotaStatusCallback
));
311 void CacheStorageManager::DeleteOriginDidClose(
313 const storage::QuotaClient::DeletionCallback
& callback
,
314 scoped_ptr
<CacheStorage
> cache_storage
,
315 base::WeakPtr
<CacheStorageManager
> cache_manager
) {
316 // TODO(jkarlin): Deleting the storage leaves any unfinished operations
317 // hanging, resulting in unresolved promises. Fix this by guaranteeing that
318 // callbacks are called in ServiceWorkerStorage.
319 cache_storage
.reset();
321 if (!cache_manager
) {
322 callback
.Run(storage::kQuotaErrorAbort
);
326 if (cache_manager
->IsMemoryBacked()) {
327 callback
.Run(storage::kQuotaStatusOk
);
331 cache_manager
->MigrateOrigin(origin
);
332 PostTaskAndReplyWithResult(
333 cache_manager
->cache_task_runner_
.get(), FROM_HERE
,
334 base::Bind(&DeleteDir
,
335 ConstructOriginPath(cache_manager
->root_path_
, origin
)),
336 base::Bind(&DeleteOriginDidDeleteDir
, callback
));
339 CacheStorageManager::CacheStorageManager(
340 const base::FilePath
& path
,
341 const scoped_refptr
<base::SequencedTaskRunner
>& cache_task_runner
,
342 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
)
344 cache_task_runner_(cache_task_runner
),
345 quota_manager_proxy_(quota_manager_proxy
),
346 weak_ptr_factory_(this) {
347 if (quota_manager_proxy_
.get()) {
348 quota_manager_proxy_
->RegisterClient(
349 new CacheStorageQuotaClient(weak_ptr_factory_
.GetWeakPtr()));
353 CacheStorage
* CacheStorageManager::FindOrCreateCacheStorage(
354 const GURL
& origin
) {
355 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
356 DCHECK(request_context_getter_
);
357 CacheStorageMap::const_iterator it
= cache_storage_map_
.find(origin
);
358 if (it
== cache_storage_map_
.end()) {
359 MigrateOrigin(origin
);
360 CacheStorage
* cache_storage
= new CacheStorage(
361 ConstructOriginPath(root_path_
, origin
), IsMemoryBacked(),
362 cache_task_runner_
.get(), request_context_getter_
, quota_manager_proxy_
,
363 blob_context_
, origin
);
364 // The map owns fetch_stores.
365 cache_storage_map_
.insert(std::make_pair(origin
, cache_storage
));
366 return cache_storage
;
372 base::FilePath
CacheStorageManager::ConstructLegacyOriginPath(
373 const base::FilePath
& root_path
,
374 const GURL
& origin
) {
375 const std::string origin_hash
= base::SHA1HashString(origin
.spec());
376 const std::string origin_hash_hex
= base::ToLowerASCII(
377 base::HexEncode(origin_hash
.c_str(), origin_hash
.length()));
378 return root_path
.AppendASCII(origin_hash_hex
);
382 base::FilePath
CacheStorageManager::ConstructOriginPath(
383 const base::FilePath
& root_path
,
384 const GURL
& origin
) {
385 const std::string identifier
= storage::GetIdentifierFromOrigin(origin
);
386 const std::string origin_hash
= base::SHA1HashString(identifier
);
387 const std::string origin_hash_hex
= base::ToLowerASCII(
388 base::HexEncode(origin_hash
.c_str(), origin_hash
.length()));
389 return root_path
.AppendASCII(origin_hash_hex
);
392 // Migrate from old origin-based path to storage identifier-based path.
393 // TODO(jsbell); Remove after a few releases.
394 void CacheStorageManager::MigrateOrigin(const GURL
& origin
) {
395 if (IsMemoryBacked())
397 base::FilePath old_path
= ConstructLegacyOriginPath(root_path_
, origin
);
398 base::FilePath new_path
= ConstructOriginPath(root_path_
, origin
);
399 cache_task_runner_
->PostTask(
400 FROM_HERE
, base::Bind(&MigrateOriginOnTaskRunner
, old_path
, new_path
));
404 void CacheStorageManager::MigrateOriginOnTaskRunner(
405 const base::FilePath
& old_path
,
406 const base::FilePath
& new_path
) {
407 if (base::PathExists(old_path
)) {
408 if (!base::PathExists(new_path
))
409 base::Move(old_path
, new_path
);
410 base::DeleteFile(old_path
, /*recursive*/ true);
414 } // namespace content