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/files/file_util.h"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/sha1.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "content/browser/service_worker/service_worker_cache.h"
17 #include "content/browser/service_worker/service_worker_cache.pb.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "net/base/directory_lister.h"
20 #include "net/base/net_errors.h"
21 #include "storage/browser/blob/blob_storage_context.h"
26 // Handles the loading and clean up of ServiceWorkerCache objects. The
27 // callback of every public method is guaranteed to be called.
28 class ServiceWorkerCacheStorage::CacheLoader {
30 typedef base::Callback<void(const scoped_refptr<ServiceWorkerCache>&)>
32 typedef base::Callback<void(bool)> BoolCallback;
33 typedef base::Callback<void(scoped_ptr<std::vector<std::string> >)>
36 CacheLoader(base::SequencedTaskRunner* cache_task_runner,
37 net::URLRequestContext* request_context,
38 base::WeakPtr<storage::BlobStorageContext> blob_context)
39 : cache_task_runner_(cache_task_runner),
40 request_context_(request_context),
41 blob_context_(blob_context) {}
43 virtual ~CacheLoader() {}
45 // Creates a ServiceWorkerCache with the given name. It does not attempt to
46 // load the backend, that happens lazily when the cache is used.
47 virtual scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
48 const std::string& cache_name) = 0;
50 // Deletes any pre-existing cache of the same name and then loads it.
51 virtual void CreateCache(const std::string& cache_name,
52 const CacheCallback& callback) = 0;
54 // After the backend has been deleted, do any extra house keeping such as
55 // removing the cache's directory.
56 virtual void CleanUpDeletedCache(const std::string& key,
57 const BoolCallback& callback) = 0;
59 // Writes the cache names (and sizes) to disk if applicable.
60 virtual void WriteIndex(const StringVector& cache_names,
61 const BoolCallback& callback) = 0;
63 // Loads the cache names from disk if applicable.
64 virtual void LoadIndex(scoped_ptr<std::vector<std::string> > cache_names,
65 const StringVectorCallback& callback) = 0;
68 scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
69 net::URLRequestContext* request_context_;
70 base::WeakPtr<storage::BlobStorageContext> blob_context_;
73 // Creates memory-only ServiceWorkerCaches. Because these caches have no
74 // persistent storage it is not safe to free them from memory if they might be
75 // used again. Therefore this class holds a reference to each cache until the
77 class ServiceWorkerCacheStorage::MemoryLoader
78 : public ServiceWorkerCacheStorage::CacheLoader {
80 MemoryLoader(base::SequencedTaskRunner* cache_task_runner,
81 net::URLRequestContext* request_context,
82 base::WeakPtr<storage::BlobStorageContext> blob_context)
83 : CacheLoader(cache_task_runner, request_context, blob_context) {}
85 virtual scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
86 const std::string& cache_name) OVERRIDE {
87 return ServiceWorkerCache::CreateMemoryCache(request_context_,
91 virtual void CreateCache(const std::string& cache_name,
92 const CacheCallback& callback) OVERRIDE {
93 scoped_refptr<ServiceWorkerCache> cache =
94 ServiceWorkerCache::CreateMemoryCache(request_context_, blob_context_);
95 cache_refs_.insert(std::make_pair(cache_name, cache));
99 virtual void CleanUpDeletedCache(const std::string& cache_name,
100 const BoolCallback& callback) OVERRIDE {
101 CacheRefMap::iterator it = cache_refs_.find(cache_name);
102 DCHECK(it != cache_refs_.end());
103 cache_refs_.erase(it);
107 virtual void WriteIndex(const StringVector& cache_names,
108 const BoolCallback& callback) OVERRIDE {
112 virtual void LoadIndex(scoped_ptr<std::vector<std::string> > cache_names,
113 const StringVectorCallback& callback) OVERRIDE {
114 callback.Run(cache_names.Pass());
118 typedef std::map<std::string, scoped_refptr<ServiceWorkerCache> > CacheRefMap;
119 virtual ~MemoryLoader() {}
121 // Keep a reference to each cache to ensure that it's not freed before the
122 // client calls ServiceWorkerCacheStorage::Delete or the CacheStorage is
124 CacheRefMap cache_refs_;
127 class ServiceWorkerCacheStorage::SimpleCacheLoader
128 : public ServiceWorkerCacheStorage::CacheLoader {
130 SimpleCacheLoader(const base::FilePath& origin_path,
131 base::SequencedTaskRunner* cache_task_runner,
132 net::URLRequestContext* request_context,
133 base::WeakPtr<storage::BlobStorageContext> blob_context)
134 : CacheLoader(cache_task_runner, request_context, blob_context),
135 origin_path_(origin_path),
136 weak_ptr_factory_(this) {}
138 virtual scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
139 const std::string& cache_name) OVERRIDE {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO);
142 return ServiceWorkerCache::CreatePersistentCache(
143 CreatePersistentCachePath(origin_path_, cache_name),
148 virtual void CreateCache(const std::string& cache_name,
149 const CacheCallback& callback) OVERRIDE {
150 DCHECK_CURRENTLY_ON(BrowserThread::IO);
152 // 1. Delete the cache's directory if it exists.
153 // (CreateCacheDeleteFilesInPool)
154 // 2. Load the cache. (LoadCreateDirectoryInPool)
156 base::FilePath cache_path =
157 CreatePersistentCachePath(origin_path_, cache_name);
159 PostTaskAndReplyWithResult(
160 cache_task_runner_.get(),
162 base::Bind(&SimpleCacheLoader::CreateCachePrepDirInPool, cache_path),
163 base::Bind(&SimpleCacheLoader::CreateCachePreppedDir,
166 weak_ptr_factory_.GetWeakPtr()));
169 static bool CreateCachePrepDirInPool(const base::FilePath& cache_path) {
170 if (base::PathExists(cache_path))
171 base::DeleteFile(cache_path, /* recursive */ true);
172 return base::CreateDirectory(cache_path);
175 static void CreateCachePreppedDir(const std::string& cache_name,
176 const CacheCallback& callback,
177 base::WeakPtr<SimpleCacheLoader> loader,
179 if (!success || !loader) {
180 callback.Run(scoped_refptr<ServiceWorkerCache>());
184 callback.Run(loader->CreateServiceWorkerCache(cache_name));
187 virtual void CleanUpDeletedCache(const std::string& cache_name,
188 const BoolCallback& callback) OVERRIDE {
189 DCHECK_CURRENTLY_ON(BrowserThread::IO);
191 // 1. Delete the cache's directory. (CleanUpDeleteCacheDirInPool)
193 base::FilePath cache_path =
194 CreatePersistentCachePath(origin_path_, cache_name);
195 cache_task_runner_->PostTask(
197 base::Bind(&SimpleCacheLoader::CleanUpDeleteCacheDirInPool,
200 base::MessageLoopProxy::current()));
203 static void CleanUpDeleteCacheDirInPool(
204 const base::FilePath& cache_path,
205 const BoolCallback& callback,
206 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
207 bool rv = base::DeleteFile(cache_path, true);
208 original_loop->PostTask(FROM_HERE, base::Bind(callback, rv));
211 virtual void WriteIndex(const StringVector& cache_names,
212 const BoolCallback& callback) OVERRIDE {
213 DCHECK_CURRENTLY_ON(BrowserThread::IO);
215 // 1. Create the index file as a string. (WriteIndex)
216 // 2. Write the file to disk. (WriteIndexWriteToFileInPool)
218 ServiceWorkerCacheStorageIndex index;
220 for (size_t i = 0u, max = cache_names.size(); i < max; ++i) {
221 ServiceWorkerCacheStorageIndex::Cache* index_cache = index.add_cache();
222 index_cache->set_name(cache_names[i]);
223 index_cache->set_size(0); // TODO(jkarlin): Make this real.
226 std::string serialized;
227 bool success = index.SerializeToString(&serialized);
230 base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp");
231 base::FilePath index_path = origin_path_.AppendASCII("index.txt");
233 cache_task_runner_->PostTask(
235 base::Bind(&SimpleCacheLoader::WriteIndexWriteToFileInPool,
240 base::MessageLoopProxy::current()));
243 static void WriteIndexWriteToFileInPool(
244 const base::FilePath& tmp_path,
245 const base::FilePath& index_path,
246 const std::string& data,
247 const BoolCallback& callback,
248 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
249 int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size());
250 if (bytes_written != implicit_cast<int>(data.size())) {
251 base::DeleteFile(tmp_path, /* recursive */ false);
252 original_loop->PostTask(FROM_HERE, base::Bind(callback, false));
255 // Atomically rename the temporary index file to become the real one.
256 bool rv = base::ReplaceFile(tmp_path, index_path, NULL);
257 original_loop->PostTask(FROM_HERE, base::Bind(callback, rv));
260 virtual void LoadIndex(scoped_ptr<std::vector<std::string> > names,
261 const StringVectorCallback& callback) OVERRIDE {
262 DCHECK_CURRENTLY_ON(BrowserThread::IO);
264 // 1. Read the file from disk. (LoadIndexReadFileInPool)
265 // 2. Parse file and return the names of the caches (LoadIndexDidReadFile)
267 base::FilePath index_path = origin_path_.AppendASCII("index.txt");
269 cache_task_runner_->PostTask(
271 base::Bind(&SimpleCacheLoader::LoadIndexReadFileInPool,
273 base::Passed(names.Pass()),
275 base::MessageLoopProxy::current()));
278 static void LoadIndexReadFileInPool(
279 const base::FilePath& index_path,
280 scoped_ptr<std::vector<std::string> > names,
281 const StringVectorCallback& callback,
282 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
284 base::ReadFileToString(index_path, &body);
286 original_loop->PostTask(FROM_HERE,
287 base::Bind(&SimpleCacheLoader::LoadIndexDidReadFile,
288 base::Passed(names.Pass()),
293 static void LoadIndexDidReadFile(scoped_ptr<std::vector<std::string> > names,
294 const StringVectorCallback& callback,
295 const std::string& serialized) {
296 DCHECK_CURRENTLY_ON(BrowserThread::IO);
298 ServiceWorkerCacheStorageIndex index;
299 if (index.ParseFromString(serialized)) {
300 for (int i = 0, max = index.cache_size(); i < max; ++i) {
301 const ServiceWorkerCacheStorageIndex::Cache& cache = index.cache(i);
302 names->push_back(cache.name());
306 // TODO(jkarlin): Delete caches that are in the directory and not returned
308 callback.Run(names.Pass());
312 virtual ~SimpleCacheLoader() {}
314 static std::string HexedHash(const std::string& value) {
315 std::string value_hash = base::SHA1HashString(value);
316 std::string valued_hexed_hash = base::StringToLowerASCII(
317 base::HexEncode(value_hash.c_str(), value_hash.length()));
318 return valued_hexed_hash;
321 static base::FilePath CreatePersistentCachePath(
322 const base::FilePath& origin_path,
323 const std::string& cache_name) {
324 return origin_path.AppendASCII(HexedHash(cache_name));
327 const base::FilePath origin_path_;
329 base::WeakPtrFactory<SimpleCacheLoader> weak_ptr_factory_;
332 ServiceWorkerCacheStorage::ServiceWorkerCacheStorage(
333 const base::FilePath& path,
335 base::SequencedTaskRunner* cache_task_runner,
336 net::URLRequestContext* request_context,
337 base::WeakPtr<storage::BlobStorageContext> blob_context)
338 : initialized_(false),
340 cache_task_runner_(cache_task_runner),
341 memory_only_(memory_only),
342 weak_factory_(this) {
344 cache_loader_.reset(new MemoryLoader(
345 cache_task_runner_.get(), request_context, blob_context));
347 cache_loader_.reset(new SimpleCacheLoader(
348 origin_path_, cache_task_runner_.get(), request_context, blob_context));
351 ServiceWorkerCacheStorage::~ServiceWorkerCacheStorage() {
354 void ServiceWorkerCacheStorage::CreateCache(
355 const std::string& cache_name,
356 const CacheAndErrorCallback& callback) {
358 LazyInit(base::Bind(&ServiceWorkerCacheStorage::CreateCache,
359 weak_factory_.GetWeakPtr(),
365 if (cache_map_.find(cache_name) != cache_map_.end()) {
366 callback.Run(scoped_refptr<ServiceWorkerCache>(),
367 CACHE_STORAGE_ERROR_EXISTS);
371 cache_loader_->CreateCache(
373 base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidCreateCache,
374 weak_factory_.GetWeakPtr(),
379 void ServiceWorkerCacheStorage::GetCache(
380 const std::string& cache_name,
381 const CacheAndErrorCallback& callback) {
382 DCHECK_CURRENTLY_ON(BrowserThread::IO);
385 LazyInit(base::Bind(&ServiceWorkerCacheStorage::GetCache,
386 weak_factory_.GetWeakPtr(),
392 scoped_refptr<ServiceWorkerCache> cache = GetLoadedCache(cache_name);
394 callback.Run(scoped_refptr<ServiceWorkerCache>(),
395 CACHE_STORAGE_ERROR_NOT_FOUND);
399 callback.Run(cache, CACHE_STORAGE_ERROR_NO_ERROR);
402 void ServiceWorkerCacheStorage::HasCache(const std::string& cache_name,
403 const BoolAndErrorCallback& callback) {
404 DCHECK_CURRENTLY_ON(BrowserThread::IO);
407 LazyInit(base::Bind(&ServiceWorkerCacheStorage::HasCache,
408 weak_factory_.GetWeakPtr(),
414 bool has_cache = cache_map_.find(cache_name) != cache_map_.end();
416 callback.Run(has_cache, CACHE_STORAGE_ERROR_NO_ERROR);
419 void ServiceWorkerCacheStorage::DeleteCache(
420 const std::string& cache_name,
421 const BoolAndErrorCallback& callback) {
422 DCHECK_CURRENTLY_ON(BrowserThread::IO);
425 LazyInit(base::Bind(&ServiceWorkerCacheStorage::DeleteCache,
426 weak_factory_.GetWeakPtr(),
432 CacheMap::iterator it = cache_map_.find(cache_name);
433 if (it == cache_map_.end()) {
434 callback.Run(false, CACHE_STORAGE_ERROR_NOT_FOUND);
438 base::WeakPtr<ServiceWorkerCache> cache = it->second;
442 cache_map_.erase(it);
444 // Delete the name from ordered_cache_names_.
445 StringVector::iterator iter = std::find(
446 ordered_cache_names_.begin(), ordered_cache_names_.end(), cache_name);
447 DCHECK(iter != ordered_cache_names_.end());
448 ordered_cache_names_.erase(iter);
451 cache_loader_->WriteIndex(
452 ordered_cache_names_,
453 base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex,
454 weak_factory_.GetWeakPtr(),
459 void ServiceWorkerCacheStorage::EnumerateCaches(
460 const StringsAndErrorCallback& callback) {
461 DCHECK_CURRENTLY_ON(BrowserThread::IO);
464 LazyInit(base::Bind(&ServiceWorkerCacheStorage::EnumerateCaches,
465 weak_factory_.GetWeakPtr(),
470 callback.Run(ordered_cache_names_, CACHE_STORAGE_ERROR_NO_ERROR);
473 // Init is run lazily so that it is called on the proper MessageLoop.
474 void ServiceWorkerCacheStorage::LazyInit(const base::Closure& callback) {
475 DCHECK_CURRENTLY_ON(BrowserThread::IO);
476 DCHECK(!initialized_);
478 init_callbacks_.push_back(callback);
480 // If this isn't the first call to LazyInit then return as the initialization
481 // has already started.
482 if (init_callbacks_.size() > 1u)
485 // 1. Get the list of cache names (async call)
486 // 2. For each cache name, load the cache (async call)
487 // 3. Once each load is complete, update the map variables.
488 // 4. Call the list of waiting callbacks.
490 scoped_ptr<std::vector<std::string> > indexed_cache_names(
491 new std::vector<std::string>());
493 cache_loader_->LoadIndex(
494 indexed_cache_names.Pass(),
495 base::Bind(&ServiceWorkerCacheStorage::LazyInitDidLoadIndex,
496 weak_factory_.GetWeakPtr(),
500 void ServiceWorkerCacheStorage::LazyInitDidLoadIndex(
501 const base::Closure& callback,
502 scoped_ptr<std::vector<std::string> > indexed_cache_names) {
503 DCHECK_CURRENTLY_ON(BrowserThread::IO);
505 for (size_t i = 0u, max = indexed_cache_names->size(); i < max; ++i) {
506 cache_map_.insert(std::make_pair(indexed_cache_names->at(i),
507 base::WeakPtr<ServiceWorkerCache>()));
508 ordered_cache_names_.push_back(indexed_cache_names->at(i));
512 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
513 it != init_callbacks_.end();
517 init_callbacks_.clear();
520 void ServiceWorkerCacheStorage::CreateCacheDidCreateCache(
521 const std::string& cache_name,
522 const CacheAndErrorCallback& callback,
523 const scoped_refptr<ServiceWorkerCache>& cache) {
524 DCHECK_CURRENTLY_ON(BrowserThread::IO);
527 callback.Run(scoped_refptr<ServiceWorkerCache>(),
528 CACHE_STORAGE_ERROR_CLOSING);
532 cache_map_.insert(std::make_pair(cache_name, cache->AsWeakPtr()));
533 ordered_cache_names_.push_back(cache_name);
535 cache_loader_->WriteIndex(
536 ordered_cache_names_,
537 base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidWriteIndex,
538 weak_factory_.GetWeakPtr(),
543 void ServiceWorkerCacheStorage::CreateCacheDidWriteIndex(
544 const CacheAndErrorCallback& callback,
545 const scoped_refptr<ServiceWorkerCache>& cache,
547 DCHECK_CURRENTLY_ON(BrowserThread::IO);
550 callback.Run(cache, CACHE_STORAGE_ERROR_NO_ERROR);
553 void ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex(
554 const std::string& cache_name,
555 const BoolAndErrorCallback& callback,
557 DCHECK_CURRENTLY_ON(BrowserThread::IO);
559 cache_loader_->CleanUpDeletedCache(
561 base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidCleanUp,
562 weak_factory_.GetWeakPtr(),
566 void ServiceWorkerCacheStorage::DeleteCacheDidCleanUp(
567 const BoolAndErrorCallback& callback,
569 DCHECK_CURRENTLY_ON(BrowserThread::IO);
571 callback.Run(true, CACHE_STORAGE_ERROR_NO_ERROR);
574 scoped_refptr<ServiceWorkerCache> ServiceWorkerCacheStorage::GetLoadedCache(
575 const std::string& cache_name) {
576 DCHECK_CURRENTLY_ON(BrowserThread::IO);
577 DCHECK(initialized_);
579 CacheMap::iterator map_iter = cache_map_.find(cache_name);
580 if (map_iter == cache_map_.end())
581 return scoped_refptr<ServiceWorkerCache>();
583 base::WeakPtr<ServiceWorkerCache> cache = map_iter->second;
586 scoped_refptr<ServiceWorkerCache> new_cache =
587 cache_loader_->CreateServiceWorkerCache(cache_name);
588 map_iter->second = new_cache->AsWeakPtr();
592 return make_scoped_refptr(cache.get());
595 } // namespace content