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/service_worker/service_worker_cache_storage.h"
9 #include "base/barrier_closure.h"
10 #include "base/files/file_util.h"
11 #include "base/files/memory_mapped_file.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/sha1.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "content/browser/service_worker/service_worker_cache.h"
18 #include "content/browser/service_worker/service_worker_cache.pb.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "net/base/directory_lister.h"
21 #include "net/base/net_errors.h"
22 #include "storage/browser/blob/blob_storage_context.h"
23 #include "storage/browser/quota/quota_manager_proxy.h"
29 void CloseAllCachesDidCloseCache(const scoped_refptr<ServiceWorkerCache>& cache,
30 const base::Closure& barrier_closure) {
31 barrier_closure.Run();
36 const char ServiceWorkerCacheStorage::kIndexFileName[] = "index.txt";
38 // Handles the loading and clean up of ServiceWorkerCache objects. The
39 // callback of every public method is guaranteed to be called.
40 class ServiceWorkerCacheStorage::CacheLoader {
42 typedef base::Callback<void(const scoped_refptr<ServiceWorkerCache>&)>
44 typedef base::Callback<void(bool)> BoolCallback;
45 typedef base::Callback<void(scoped_ptr<std::vector<std::string> >)>
49 base::SequencedTaskRunner* cache_task_runner,
50 net::URLRequestContext* request_context,
51 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
52 base::WeakPtr<storage::BlobStorageContext> blob_context,
54 : cache_task_runner_(cache_task_runner),
55 request_context_(request_context),
56 quota_manager_proxy_(quota_manager_proxy),
57 blob_context_(blob_context),
59 DCHECK(!origin_.is_empty());
62 virtual ~CacheLoader() {}
64 // Creates a ServiceWorkerCache with the given name. It does not attempt to
65 // load the backend, that happens lazily when the cache is used.
66 virtual scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
67 const std::string& cache_name) = 0;
69 // Deletes any pre-existing cache of the same name and then loads it.
70 virtual void CreateCache(const std::string& cache_name,
71 const CacheCallback& callback) = 0;
73 // After the backend has been deleted, do any extra house keeping such as
74 // removing the cache's directory.
75 virtual void CleanUpDeletedCache(const std::string& key,
76 const BoolCallback& callback) = 0;
78 // Writes the cache names (and sizes) to disk if applicable.
79 virtual void WriteIndex(const StringVector& cache_names,
80 const BoolCallback& callback) = 0;
82 // Loads the cache names from disk if applicable.
83 virtual void LoadIndex(scoped_ptr<std::vector<std::string> > cache_names,
84 const StringVectorCallback& callback) = 0;
87 scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
88 net::URLRequestContext* request_context_;
89 scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
90 base::WeakPtr<storage::BlobStorageContext> blob_context_;
94 // Creates memory-only ServiceWorkerCaches. Because these caches have no
95 // persistent storage it is not safe to free them from memory if they might be
96 // used again. Therefore this class holds a reference to each cache until the
98 class ServiceWorkerCacheStorage::MemoryLoader
99 : public ServiceWorkerCacheStorage::CacheLoader {
102 base::SequencedTaskRunner* cache_task_runner,
103 net::URLRequestContext* request_context,
104 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
105 base::WeakPtr<storage::BlobStorageContext> blob_context,
107 : CacheLoader(cache_task_runner,
113 scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
114 const std::string& cache_name) override {
115 return ServiceWorkerCache::CreateMemoryCache(
116 origin_, request_context_, quota_manager_proxy_, blob_context_);
119 void CreateCache(const std::string& cache_name,
120 const CacheCallback& callback) override {
121 scoped_refptr<ServiceWorkerCache> cache =
122 CreateServiceWorkerCache(cache_name);
123 cache_refs_.insert(std::make_pair(cache_name, cache));
127 void CleanUpDeletedCache(const std::string& cache_name,
128 const BoolCallback& callback) override {
129 CacheRefMap::iterator it = cache_refs_.find(cache_name);
130 DCHECK(it != cache_refs_.end());
131 cache_refs_.erase(it);
135 void WriteIndex(const StringVector& cache_names,
136 const BoolCallback& callback) override {
140 void LoadIndex(scoped_ptr<std::vector<std::string>> cache_names,
141 const StringVectorCallback& callback) override {
142 callback.Run(cache_names.Pass());
146 typedef std::map<std::string, scoped_refptr<ServiceWorkerCache> > CacheRefMap;
147 ~MemoryLoader() override {}
149 // Keep a reference to each cache to ensure that it's not freed before the
150 // client calls ServiceWorkerCacheStorage::Delete or the CacheStorage is
152 CacheRefMap cache_refs_;
155 class ServiceWorkerCacheStorage::SimpleCacheLoader
156 : public ServiceWorkerCacheStorage::CacheLoader {
159 const base::FilePath& origin_path,
160 base::SequencedTaskRunner* cache_task_runner,
161 net::URLRequestContext* request_context,
162 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
163 base::WeakPtr<storage::BlobStorageContext> blob_context,
165 : CacheLoader(cache_task_runner,
170 origin_path_(origin_path),
171 weak_ptr_factory_(this) {}
173 scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
174 const std::string& cache_name) override {
175 DCHECK_CURRENTLY_ON(BrowserThread::IO);
177 return ServiceWorkerCache::CreatePersistentCache(
179 CreatePersistentCachePath(origin_path_, cache_name),
181 quota_manager_proxy_,
185 void CreateCache(const std::string& cache_name,
186 const CacheCallback& callback) override {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO);
189 // 1. Delete the cache's directory if it exists.
190 // (CreateCacheDeleteFilesInPool)
191 // 2. Load the cache. (LoadCreateDirectoryInPool)
193 base::FilePath cache_path =
194 CreatePersistentCachePath(origin_path_, cache_name);
196 PostTaskAndReplyWithResult(
197 cache_task_runner_.get(),
199 base::Bind(&SimpleCacheLoader::CreateCachePrepDirInPool, cache_path),
200 base::Bind(&SimpleCacheLoader::CreateCachePreppedDir,
203 weak_ptr_factory_.GetWeakPtr()));
206 static bool CreateCachePrepDirInPool(const base::FilePath& cache_path) {
207 if (base::PathExists(cache_path))
208 base::DeleteFile(cache_path, /* recursive */ true);
209 return base::CreateDirectory(cache_path);
212 static void CreateCachePreppedDir(const std::string& cache_name,
213 const CacheCallback& callback,
214 base::WeakPtr<SimpleCacheLoader> loader,
216 if (!success || !loader) {
217 callback.Run(scoped_refptr<ServiceWorkerCache>());
221 callback.Run(loader->CreateServiceWorkerCache(cache_name));
224 void CleanUpDeletedCache(const std::string& cache_name,
225 const BoolCallback& callback) override {
226 DCHECK_CURRENTLY_ON(BrowserThread::IO);
228 // 1. Delete the cache's directory. (CleanUpDeleteCacheDirInPool)
230 base::FilePath cache_path =
231 CreatePersistentCachePath(origin_path_, cache_name);
232 cache_task_runner_->PostTask(
234 base::Bind(&SimpleCacheLoader::CleanUpDeleteCacheDirInPool,
237 base::MessageLoopProxy::current()));
240 static void CleanUpDeleteCacheDirInPool(
241 const base::FilePath& cache_path,
242 const BoolCallback& callback,
243 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
244 bool rv = base::DeleteFile(cache_path, true);
245 original_loop->PostTask(FROM_HERE, base::Bind(callback, rv));
248 void WriteIndex(const StringVector& cache_names,
249 const BoolCallback& callback) override {
250 DCHECK_CURRENTLY_ON(BrowserThread::IO);
252 // 1. Create the index file as a string. (WriteIndex)
253 // 2. Write the file to disk. (WriteIndexWriteToFileInPool)
255 ServiceWorkerCacheStorageIndex index;
256 index.set_origin(origin_.spec());
258 for (size_t i = 0u, max = cache_names.size(); i < max; ++i) {
259 ServiceWorkerCacheStorageIndex::Cache* index_cache = index.add_cache();
260 index_cache->set_name(cache_names[i]);
263 std::string serialized;
264 bool success = index.SerializeToString(&serialized);
267 base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp");
268 base::FilePath index_path =
269 origin_path_.AppendASCII(ServiceWorkerCacheStorage::kIndexFileName);
271 cache_task_runner_->PostTask(
273 base::Bind(&SimpleCacheLoader::WriteIndexWriteToFileInPool,
278 base::MessageLoopProxy::current()));
281 static void WriteIndexWriteToFileInPool(
282 const base::FilePath& tmp_path,
283 const base::FilePath& index_path,
284 const std::string& data,
285 const BoolCallback& callback,
286 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
287 int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size());
288 if (bytes_written != implicit_cast<int>(data.size())) {
289 base::DeleteFile(tmp_path, /* recursive */ false);
290 original_loop->PostTask(FROM_HERE, base::Bind(callback, false));
293 // Atomically rename the temporary index file to become the real one.
294 bool rv = base::ReplaceFile(tmp_path, index_path, NULL);
295 original_loop->PostTask(FROM_HERE, base::Bind(callback, rv));
298 void LoadIndex(scoped_ptr<std::vector<std::string>> names,
299 const StringVectorCallback& callback) override {
300 DCHECK_CURRENTLY_ON(BrowserThread::IO);
302 // 1. Read the file from disk. (LoadIndexReadFileInPool)
303 // 2. Parse file and return the names of the caches (LoadIndexDidReadFile)
305 base::FilePath index_path =
306 origin_path_.AppendASCII(ServiceWorkerCacheStorage::kIndexFileName);
308 cache_task_runner_->PostTask(
310 base::Bind(&SimpleCacheLoader::LoadIndexReadFileInPool,
312 base::Passed(names.Pass()),
314 base::MessageLoopProxy::current()));
317 static void LoadIndexReadFileInPool(
318 const base::FilePath& index_path,
319 scoped_ptr<std::vector<std::string> > names,
320 const StringVectorCallback& callback,
321 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
323 base::ReadFileToString(index_path, &body);
325 original_loop->PostTask(FROM_HERE,
326 base::Bind(&SimpleCacheLoader::LoadIndexDidReadFile,
327 base::Passed(names.Pass()),
332 static void LoadIndexDidReadFile(scoped_ptr<std::vector<std::string> > names,
333 const StringVectorCallback& callback,
334 const std::string& serialized) {
335 DCHECK_CURRENTLY_ON(BrowserThread::IO);
337 ServiceWorkerCacheStorageIndex index;
338 if (index.ParseFromString(serialized)) {
339 for (int i = 0, max = index.cache_size(); i < max; ++i) {
340 const ServiceWorkerCacheStorageIndex::Cache& cache = index.cache(i);
341 names->push_back(cache.name());
345 // TODO(jkarlin): Delete caches that are in the directory and not returned
347 callback.Run(names.Pass());
351 ~SimpleCacheLoader() override {}
353 static std::string HexedHash(const std::string& value) {
354 std::string value_hash = base::SHA1HashString(value);
355 std::string valued_hexed_hash = base::StringToLowerASCII(
356 base::HexEncode(value_hash.c_str(), value_hash.length()));
357 return valued_hexed_hash;
360 static base::FilePath CreatePersistentCachePath(
361 const base::FilePath& origin_path,
362 const std::string& cache_name) {
363 return origin_path.AppendASCII(HexedHash(cache_name));
366 const base::FilePath origin_path_;
368 base::WeakPtrFactory<SimpleCacheLoader> weak_ptr_factory_;
371 ServiceWorkerCacheStorage::ServiceWorkerCacheStorage(
372 const base::FilePath& path,
374 base::SequencedTaskRunner* cache_task_runner,
375 net::URLRequestContext* request_context,
376 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
377 base::WeakPtr<storage::BlobStorageContext> blob_context,
379 : initialized_(false),
381 cache_task_runner_(cache_task_runner),
382 memory_only_(memory_only),
383 weak_factory_(this) {
385 cache_loader_.reset(new MemoryLoader(cache_task_runner_.get(),
391 cache_loader_.reset(new SimpleCacheLoader(origin_path_,
392 cache_task_runner_.get(),
399 ServiceWorkerCacheStorage::~ServiceWorkerCacheStorage() {
402 void ServiceWorkerCacheStorage::OpenCache(
403 const std::string& cache_name,
404 const CacheAndErrorCallback& callback) {
405 DCHECK_CURRENTLY_ON(BrowserThread::IO);
408 LazyInit(base::Bind(&ServiceWorkerCacheStorage::OpenCache,
409 weak_factory_.GetWeakPtr(),
415 scoped_refptr<ServiceWorkerCache> cache = GetLoadedCache(cache_name);
417 callback.Run(cache, CACHE_STORAGE_ERROR_NO_ERROR);
421 cache_loader_->CreateCache(
423 base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidCreateCache,
424 weak_factory_.GetWeakPtr(),
429 void ServiceWorkerCacheStorage::HasCache(const std::string& cache_name,
430 const BoolAndErrorCallback& callback) {
431 DCHECK_CURRENTLY_ON(BrowserThread::IO);
434 LazyInit(base::Bind(&ServiceWorkerCacheStorage::HasCache,
435 weak_factory_.GetWeakPtr(),
441 bool has_cache = cache_map_.find(cache_name) != cache_map_.end();
443 callback.Run(has_cache, CACHE_STORAGE_ERROR_NO_ERROR);
446 void ServiceWorkerCacheStorage::DeleteCache(
447 const std::string& cache_name,
448 const BoolAndErrorCallback& callback) {
449 DCHECK_CURRENTLY_ON(BrowserThread::IO);
452 LazyInit(base::Bind(&ServiceWorkerCacheStorage::DeleteCache,
453 weak_factory_.GetWeakPtr(),
459 CacheMap::iterator it = cache_map_.find(cache_name);
460 if (it == cache_map_.end()) {
461 callback.Run(false, CACHE_STORAGE_ERROR_NOT_FOUND);
465 base::WeakPtr<ServiceWorkerCache> cache = it->second;
466 cache_map_.erase(it);
468 // Delete the name from ordered_cache_names_.
469 StringVector::iterator iter = std::find(
470 ordered_cache_names_.begin(), ordered_cache_names_.end(), cache_name);
471 DCHECK(iter != ordered_cache_names_.end());
472 ordered_cache_names_.erase(iter);
474 base::Closure closure =
475 base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidClose,
476 weak_factory_.GetWeakPtr(), cache_name, callback,
477 ordered_cache_names_, make_scoped_refptr(cache.get()));
480 cache->Close(closure);
487 void ServiceWorkerCacheStorage::EnumerateCaches(
488 const StringsAndErrorCallback& callback) {
489 DCHECK_CURRENTLY_ON(BrowserThread::IO);
492 LazyInit(base::Bind(&ServiceWorkerCacheStorage::EnumerateCaches,
493 weak_factory_.GetWeakPtr(),
498 callback.Run(ordered_cache_names_, CACHE_STORAGE_ERROR_NO_ERROR);
501 void ServiceWorkerCacheStorage::CloseAllCaches(const base::Closure& callback) {
502 DCHECK_CURRENTLY_ON(BrowserThread::IO);
509 int live_cache_count = 0;
510 for (const auto& key_value : cache_map_) {
511 if (key_value.second)
512 live_cache_count += 1;
515 if (live_cache_count == 0) {
520 // The closure might modify this object so delay calling it until after
521 // iterating through cache_map_ by adding one to the barrier.
522 base::Closure barrier_closure =
523 base::BarrierClosure(live_cache_count + 1, base::Bind(callback));
525 for (auto& key_value : cache_map_) {
526 if (key_value.second) {
527 key_value.second->Close(base::Bind(
528 CloseAllCachesDidCloseCache,
529 make_scoped_refptr(key_value.second.get()), barrier_closure));
533 barrier_closure.Run();
536 int64 ServiceWorkerCacheStorage::MemoryBackedSize() const {
537 DCHECK_CURRENTLY_ON(BrowserThread::IO);
539 if (!initialized_ || !memory_only_)
543 for (auto& key_value : cache_map_) {
544 if (key_value.second)
545 sum += key_value.second->MemoryBackedSize();
550 // Init is run lazily so that it is called on the proper MessageLoop.
551 void ServiceWorkerCacheStorage::LazyInit(const base::Closure& callback) {
552 DCHECK_CURRENTLY_ON(BrowserThread::IO);
553 DCHECK(!initialized_);
555 init_callbacks_.push_back(callback);
557 // If this isn't the first call to LazyInit then return as the initialization
558 // has already started.
559 if (init_callbacks_.size() > 1u)
562 // 1. Get the list of cache names (async call)
563 // 2. For each cache name, load the cache (async call)
564 // 3. Once each load is complete, update the map variables.
565 // 4. Call the list of waiting callbacks.
567 scoped_ptr<std::vector<std::string> > indexed_cache_names(
568 new std::vector<std::string>());
570 cache_loader_->LoadIndex(
571 indexed_cache_names.Pass(),
572 base::Bind(&ServiceWorkerCacheStorage::LazyInitDidLoadIndex,
573 weak_factory_.GetWeakPtr(),
577 void ServiceWorkerCacheStorage::LazyInitDidLoadIndex(
578 const base::Closure& callback,
579 scoped_ptr<std::vector<std::string> > indexed_cache_names) {
580 DCHECK_CURRENTLY_ON(BrowserThread::IO);
582 for (size_t i = 0u, max = indexed_cache_names->size(); i < max; ++i) {
583 cache_map_.insert(std::make_pair(indexed_cache_names->at(i),
584 base::WeakPtr<ServiceWorkerCache>()));
585 ordered_cache_names_.push_back(indexed_cache_names->at(i));
589 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
590 it != init_callbacks_.end();
594 init_callbacks_.clear();
597 void ServiceWorkerCacheStorage::CreateCacheDidCreateCache(
598 const std::string& cache_name,
599 const CacheAndErrorCallback& callback,
600 const scoped_refptr<ServiceWorkerCache>& cache) {
601 DCHECK_CURRENTLY_ON(BrowserThread::IO);
604 callback.Run(scoped_refptr<ServiceWorkerCache>(),
605 CACHE_STORAGE_ERROR_CLOSING);
609 cache_map_.insert(std::make_pair(cache_name, cache->AsWeakPtr()));
610 ordered_cache_names_.push_back(cache_name);
612 cache_loader_->WriteIndex(
613 ordered_cache_names_,
614 base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidWriteIndex,
615 weak_factory_.GetWeakPtr(),
620 void ServiceWorkerCacheStorage::CreateCacheDidWriteIndex(
621 const CacheAndErrorCallback& callback,
622 const scoped_refptr<ServiceWorkerCache>& cache,
624 DCHECK_CURRENTLY_ON(BrowserThread::IO);
627 callback.Run(cache, CACHE_STORAGE_ERROR_NO_ERROR);
630 void ServiceWorkerCacheStorage::DeleteCacheDidClose(
631 const std::string& cache_name,
632 const BoolAndErrorCallback& callback,
633 const StringVector& ordered_cache_names,
634 const scoped_refptr<ServiceWorkerCache>& cache /* might be null */) {
635 cache_loader_->WriteIndex(
637 base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex,
638 weak_factory_.GetWeakPtr(), cache_name, callback));
641 void ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex(
642 const std::string& cache_name,
643 const BoolAndErrorCallback& callback,
645 DCHECK_CURRENTLY_ON(BrowserThread::IO);
647 cache_loader_->CleanUpDeletedCache(
649 base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidCleanUp,
650 weak_factory_.GetWeakPtr(),
654 void ServiceWorkerCacheStorage::DeleteCacheDidCleanUp(
655 const BoolAndErrorCallback& callback,
657 DCHECK_CURRENTLY_ON(BrowserThread::IO);
659 callback.Run(true, CACHE_STORAGE_ERROR_NO_ERROR);
662 scoped_refptr<ServiceWorkerCache> ServiceWorkerCacheStorage::GetLoadedCache(
663 const std::string& cache_name) {
664 DCHECK_CURRENTLY_ON(BrowserThread::IO);
665 DCHECK(initialized_);
667 CacheMap::iterator map_iter = cache_map_.find(cache_name);
668 if (map_iter == cache_map_.end())
669 return scoped_refptr<ServiceWorkerCache>();
671 base::WeakPtr<ServiceWorkerCache> cache = map_iter->second;
674 scoped_refptr<ServiceWorkerCache> new_cache =
675 cache_loader_->CreateServiceWorkerCache(cache_name);
676 map_iter->second = new_cache->AsWeakPtr();
680 return make_scoped_refptr(cache.get());
683 } // namespace content