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/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/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "content/browser/service_worker/service_worker_cache.h"
16 #include "content/browser/service_worker/service_worker_cache.pb.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "net/base/directory_lister.h"
19 #include "net/base/net_errors.h"
20 #include "webkit/browser/blob/blob_storage_context.h"
24 // Handles the loading and clean up of ServiceWorkerCache objects.
25 class ServiceWorkerCacheStorage::CacheLoader
26 : public base::RefCountedThreadSafe<
27 ServiceWorkerCacheStorage::CacheLoader> {
29 typedef base::Callback<void(scoped_ptr<ServiceWorkerCache>)> CacheCallback;
30 typedef base::Callback<void(bool)> BoolCallback;
31 typedef base::Callback<void(scoped_ptr<std::vector<std::string> >)>
35 base::SequencedTaskRunner* cache_task_runner,
36 net::URLRequestContext* request_context,
37 base::WeakPtr<webkit_blob::BlobStorageContext> blob_context)
38 : cache_task_runner_(cache_task_runner),
39 request_context_(request_context),
40 blob_context_(blob_context) {}
42 // Loads the given cache_name, the cache is NULL if it fails. If the cache
43 // doesn't exist a new one is created.
44 virtual void LoadCache(const std::string& cache_name,
45 const CacheCallback& callback) = 0;
47 // Deletes any pre-existing cache of the same name and then loads it.
48 virtual void CreateCache(const std::string& cache_name,
49 const CacheCallback& callback) = 0;
51 // After the backend has been deleted, do any extra house keeping such as
52 // removing the cache's directory.
53 virtual void CleanUpDeletedCache(const std::string& key,
54 const BoolCallback& callback) = 0;
56 // Writes the cache names (and sizes) to disk if applicable.
57 virtual void WriteIndex(CacheMap* caches, const BoolCallback& callback) = 0;
59 // Loads the cache names from disk if applicable.
60 virtual void LoadIndex(scoped_ptr<std::vector<std::string> > cache_names,
61 const StringsCallback& callback) = 0;
64 friend class base::RefCountedThreadSafe<
65 ServiceWorkerCacheStorage::CacheLoader>;
67 virtual ~CacheLoader() {};
68 virtual void LoadCacheImpl(const std::string&) {}
70 scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
71 net::URLRequestContext* request_context_;
72 base::WeakPtr<webkit_blob::BlobStorageContext> blob_context_;
75 class ServiceWorkerCacheStorage::MemoryLoader
76 : public ServiceWorkerCacheStorage::CacheLoader {
79 base::SequencedTaskRunner* cache_task_runner,
80 net::URLRequestContext* request_context,
81 base::WeakPtr<webkit_blob::BlobStorageContext> blob_context)
82 : CacheLoader(cache_task_runner, request_context, blob_context) {}
83 virtual void LoadCache(const std::string& cache_name,
84 const CacheCallback& callback) OVERRIDE {
88 virtual void CreateCache(const std::string& cache_name,
89 const CacheCallback& callback) OVERRIDE {
90 scoped_ptr<ServiceWorkerCache> cache =
91 ServiceWorkerCache::CreateMemoryCache(
92 cache_name, request_context_, blob_context_);
93 callback.Run(cache.Pass());
96 virtual void CleanUpDeletedCache(const std::string& cache_name,
97 const BoolCallback& callback) OVERRIDE {
101 virtual void WriteIndex(CacheMap* caches,
102 const BoolCallback& callback) OVERRIDE {
106 virtual void LoadIndex(scoped_ptr<std::vector<std::string> > cache_names,
107 const StringsCallback& callback) OVERRIDE {
108 callback.Run(cache_names.Pass());
112 virtual ~MemoryLoader() {}
115 class ServiceWorkerCacheStorage::SimpleCacheLoader
116 : public ServiceWorkerCacheStorage::CacheLoader {
118 SimpleCacheLoader(const base::FilePath& origin_path,
119 base::SequencedTaskRunner* cache_task_runner,
120 net::URLRequestContext* request_context,
121 base::WeakPtr<webkit_blob::BlobStorageContext> blob_context)
122 : CacheLoader(cache_task_runner, request_context, blob_context),
123 origin_path_(origin_path) {}
125 virtual void LoadCache(const std::string& cache_name,
126 const CacheCallback& callback) OVERRIDE {
127 DCHECK_CURRENTLY_ON(BrowserThread::IO);
129 // 1. Create the cache's directory if necessary. (LoadCreateDirectoryInPool)
130 // 2. Create the cache object. (LoadDidCreateDirectory)
132 cache_task_runner_->PostTask(
134 base::Bind(&SimpleCacheLoader::LoadCreateDirectoryInPool,
136 CreatePersistentCachePath(origin_path_, cache_name),
139 base::MessageLoopProxy::current()));
142 void LoadCreateDirectoryInPool(
143 const base::FilePath& path,
144 const std::string& cache_name,
145 const CacheCallback& callback,
146 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
147 DCHECK(cache_task_runner_->RunsTasksOnCurrentThread());
149 bool rv = base::CreateDirectory(path);
150 original_loop->PostTask(
152 base::Bind(&SimpleCacheLoader::LoadDidCreateDirectory,
159 void LoadDidCreateDirectory(const std::string& cache_name,
160 const CacheCallback& callback,
162 DCHECK_CURRENTLY_ON(BrowserThread::IO);
165 callback.Run(scoped_ptr<ServiceWorkerCache>());
169 scoped_ptr<ServiceWorkerCache> cache =
170 ServiceWorkerCache::CreatePersistentCache(
171 CreatePersistentCachePath(origin_path_, cache_name),
175 callback.Run(cache.Pass());
178 virtual void CreateCache(const std::string& cache_name,
179 const CacheCallback& callback) OVERRIDE {
180 DCHECK_CURRENTLY_ON(BrowserThread::IO);
182 // 1. Delete the cache's directory if it exists.
183 // (CreateCacheDeleteFilesInPool)
184 // 2. Load the cache. (LoadCreateDirectoryInPool)
186 base::FilePath cache_path =
187 CreatePersistentCachePath(origin_path_, cache_name);
189 cache_task_runner_->PostTask(
191 base::Bind(&SimpleCacheLoader::CreateCacheDeleteFilesInPool,
196 base::MessageLoopProxy::current()));
199 void CreateCacheDeleteFilesInPool(
200 const base::FilePath& cache_path,
201 const std::string& cache_name,
202 const CacheCallback& callback,
203 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
204 DCHECK(cache_task_runner_->RunsTasksOnCurrentThread());
206 base::FilePath path(cache_path);
207 if (base::PathExists(path))
208 base::DeleteFile(path, /* recursive */ true);
210 // Jump straight into LoadCache on the same thread.
211 cache_task_runner_->PostTask(
213 base::Bind(&SimpleCacheLoader::LoadCreateDirectoryInPool,
221 virtual void CleanUpDeletedCache(const std::string& cache_name,
222 const BoolCallback& callback) OVERRIDE {
223 DCHECK_CURRENTLY_ON(BrowserThread::IO);
225 // 1. Delete the cache's directory. (CleanUpDeleteCacheDirInPool)
227 base::FilePath cache_path =
228 CreatePersistentCachePath(origin_path_, cache_name);
229 cache_task_runner_->PostTask(
231 base::Bind(&SimpleCacheLoader::CleanUpDeleteCacheDirInPool,
235 base::MessageLoopProxy::current()));
238 void CleanUpDeleteCacheDirInPool(
239 const base::FilePath& cache_path,
240 const BoolCallback& callback,
241 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
242 DCHECK(cache_task_runner_->RunsTasksOnCurrentThread());
244 bool rv = base::DeleteFile(cache_path, true);
245 original_loop->PostTask(FROM_HERE, base::Bind(callback, rv));
248 virtual void WriteIndex(CacheMap* caches,
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;
257 for (CacheMap::const_iterator iter(caches); !iter.IsAtEnd();
259 const ServiceWorkerCache* cache = iter.GetCurrentValue();
260 ServiceWorkerCacheStorageIndex::Cache* index_cache = index.add_cache();
261 index_cache->set_name(cache->name());
262 index_cache->set_size(0); // TODO(jkarlin): Make this real.
265 std::string serialized;
266 bool success = index.SerializeToString(&serialized);
269 base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp");
270 base::FilePath index_path = origin_path_.AppendASCII("index.txt");
272 cache_task_runner_->PostTask(
274 base::Bind(&SimpleCacheLoader::WriteIndexWriteToFileInPool,
281 base::MessageLoopProxy::current()));
284 void WriteIndexWriteToFileInPool(
285 const base::FilePath& tmp_path,
286 const base::FilePath& index_path,
287 const std::string& data,
289 const BoolCallback& callback,
290 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
291 DCHECK(cache_task_runner_->RunsTasksOnCurrentThread());
293 int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size());
294 if (bytes_written != implicit_cast<int>(data.size())) {
295 base::DeleteFile(tmp_path, /* recursive */ false);
296 original_loop->PostTask(FROM_HERE, base::Bind(callback, false));
299 // Atomically rename the temporary index file to become the real one.
300 bool rv = base::ReplaceFile(tmp_path, index_path, NULL);
301 original_loop->PostTask(FROM_HERE, base::Bind(callback, rv));
304 virtual void LoadIndex(scoped_ptr<std::vector<std::string> > names,
305 const StringsCallback& callback) OVERRIDE {
306 DCHECK_CURRENTLY_ON(BrowserThread::IO);
308 // 1. Read the file from disk. (LoadIndexReadFileInPool)
309 // 2. Parse file and return the names of the caches (LoadIndexDidReadFile)
311 base::FilePath index_path = origin_path_.AppendASCII("index.txt");
313 cache_task_runner_->PostTask(
315 base::Bind(&SimpleCacheLoader::LoadIndexReadFileInPool,
318 base::Passed(names.Pass()),
320 base::MessageLoopProxy::current()));
323 void LoadIndexReadFileInPool(
324 const base::FilePath& index_path,
325 scoped_ptr<std::vector<std::string> > names,
326 const StringsCallback& callback,
327 const scoped_refptr<base::MessageLoopProxy>& original_loop) {
328 DCHECK(cache_task_runner_->RunsTasksOnCurrentThread());
331 base::ReadFileToString(index_path, &body);
333 original_loop->PostTask(FROM_HERE,
334 base::Bind(&SimpleCacheLoader::LoadIndexDidReadFile,
336 base::Passed(names.Pass()),
341 void LoadIndexDidReadFile(scoped_ptr<std::vector<std::string> > names,
342 const StringsCallback& callback,
343 const std::string& serialized) {
344 DCHECK_CURRENTLY_ON(BrowserThread::IO);
346 ServiceWorkerCacheStorageIndex index;
347 index.ParseFromString(serialized);
349 for (int i = 0, max = index.cache_size(); i < max; ++i) {
350 const ServiceWorkerCacheStorageIndex::Cache& cache = index.cache(i);
351 names->push_back(cache.name());
354 // TODO(jkarlin): Delete caches that are in the directory and not returned
356 callback.Run(names.Pass());
360 virtual ~SimpleCacheLoader() {}
362 std::string HexedHash(const std::string& value) {
363 std::string value_hash = base::SHA1HashString(value);
364 std::string valued_hexed_hash = base::StringToLowerASCII(
365 base::HexEncode(value_hash.c_str(), value_hash.length()));
366 return valued_hexed_hash;
369 base::FilePath CreatePersistentCachePath(const base::FilePath& origin_path,
370 const std::string& cache_name) {
371 return origin_path.AppendASCII(HexedHash(cache_name));
374 const base::FilePath origin_path_;
377 ServiceWorkerCacheStorage::ServiceWorkerCacheStorage(
378 const base::FilePath& path,
380 base::SequencedTaskRunner* cache_task_runner,
381 net::URLRequestContext* request_context,
382 base::WeakPtr<webkit_blob::BlobStorageContext> blob_context)
383 : initialized_(false),
385 cache_task_runner_(cache_task_runner),
386 weak_factory_(this) {
389 new MemoryLoader(cache_task_runner_, request_context, blob_context);
391 cache_loader_ = new SimpleCacheLoader(
392 origin_path_, cache_task_runner_, request_context, blob_context);
395 ServiceWorkerCacheStorage::~ServiceWorkerCacheStorage() {
398 void ServiceWorkerCacheStorage::CreateCache(
399 const std::string& cache_name,
400 const CacheAndErrorCallback& callback) {
402 LazyInit(base::Bind(&ServiceWorkerCacheStorage::CreateCache,
403 weak_factory_.GetWeakPtr(),
409 if (cache_name.empty()) {
410 callback.Run(0, CACHE_STORAGE_ERROR_EMPTY_KEY);
414 if (GetLoadedCache(cache_name)) {
415 callback.Run(0, CACHE_STORAGE_ERROR_EXISTS);
419 cache_loader_->CreateCache(
421 base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidCreateCache,
422 weak_factory_.GetWeakPtr(),
427 void ServiceWorkerCacheStorage::GetCache(
428 const std::string& cache_name,
429 const CacheAndErrorCallback& callback) {
430 DCHECK_CURRENTLY_ON(BrowserThread::IO);
433 LazyInit(base::Bind(&ServiceWorkerCacheStorage::GetCache,
434 weak_factory_.GetWeakPtr(),
440 if (cache_name.empty()) {
441 callback.Run(0, CACHE_STORAGE_ERROR_EMPTY_KEY);
445 ServiceWorkerCache* cache = GetLoadedCache(cache_name);
447 callback.Run(0, CACHE_STORAGE_ERROR_NOT_FOUND);
451 cache->CreateBackend(base::Bind(&ServiceWorkerCacheStorage::DidCreateBackend,
452 weak_factory_.GetWeakPtr(),
457 void ServiceWorkerCacheStorage::HasCache(const std::string& cache_name,
458 const BoolAndErrorCallback& callback) {
459 DCHECK_CURRENTLY_ON(BrowserThread::IO);
462 LazyInit(base::Bind(&ServiceWorkerCacheStorage::HasCache,
463 weak_factory_.GetWeakPtr(),
469 if (cache_name.empty()) {
470 callback.Run(false, CACHE_STORAGE_ERROR_EMPTY_KEY);
474 bool has_cache = GetLoadedCache(cache_name) != NULL;
476 callback.Run(has_cache, CACHE_STORAGE_ERROR_NO_ERROR);
479 void ServiceWorkerCacheStorage::DeleteCache(
480 const std::string& cache_name,
481 const BoolAndErrorCallback& callback) {
482 DCHECK_CURRENTLY_ON(BrowserThread::IO);
485 LazyInit(base::Bind(&ServiceWorkerCacheStorage::DeleteCache,
486 weak_factory_.GetWeakPtr(),
492 if (cache_name.empty()) {
493 callback.Run(false, CACHE_STORAGE_ERROR_EMPTY_KEY);
497 ServiceWorkerCache* cache = GetLoadedCache(cache_name);
499 callback.Run(false, CACHE_STORAGE_ERROR_NOT_FOUND);
503 name_map_.erase(cache_name);
504 cache_map_.Remove(cache->id()); // deletes cache
507 cache_loader_->WriteIndex(
509 base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex,
510 weak_factory_.GetWeakPtr(),
515 void ServiceWorkerCacheStorage::EnumerateCaches(
516 const StringsAndErrorCallback& callback) {
517 DCHECK_CURRENTLY_ON(BrowserThread::IO);
520 LazyInit(base::Bind(&ServiceWorkerCacheStorage::EnumerateCaches,
521 weak_factory_.GetWeakPtr(),
526 std::vector<std::string> names;
527 for (NameMap::const_iterator it = name_map_.begin(); it != name_map_.end();
529 names.push_back(it->first);
532 callback.Run(names, CACHE_STORAGE_ERROR_NO_ERROR);
535 void ServiceWorkerCacheStorage::DidCreateBackend(
536 base::WeakPtr<ServiceWorkerCache> cache,
537 const CacheAndErrorCallback& callback,
539 DCHECK_CURRENTLY_ON(BrowserThread::IO);
541 if (!success || !cache) {
542 // TODO(jkarlin): This should delete the directory and try again in case
543 // the cache is simply corrupt.
544 callback.Run(0, CACHE_STORAGE_ERROR_STORAGE);
547 callback.Run(cache->id(), CACHE_STORAGE_ERROR_NO_ERROR);
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 if (indexed_cache_names->empty()) {
587 std::vector<std::string>::const_iterator iter = indexed_cache_names->begin();
588 std::vector<std::string>::const_iterator iter_next = iter + 1;
590 cache_loader_->LoadCache(
592 base::Bind(&ServiceWorkerCacheStorage::LazyInitIterateAndLoadCacheName,
593 weak_factory_.GetWeakPtr(),
595 base::Passed(indexed_cache_names.Pass()),
599 void ServiceWorkerCacheStorage::LazyInitIterateAndLoadCacheName(
600 const base::Closure& callback,
601 scoped_ptr<std::vector<std::string> > indexed_cache_names,
602 const std::vector<std::string>::const_iterator& iter,
603 scoped_ptr<ServiceWorkerCache> cache) {
604 DCHECK_CURRENTLY_ON(BrowserThread::IO);
607 AddCacheToMaps(cache.Pass());
609 if (iter == indexed_cache_names->end()) {
614 std::vector<std::string>::const_iterator iter_next = iter + 1;
615 cache_loader_->LoadCache(
617 base::Bind(&ServiceWorkerCacheStorage::LazyInitIterateAndLoadCacheName,
618 weak_factory_.GetWeakPtr(),
620 base::Passed(indexed_cache_names.Pass()),
624 void ServiceWorkerCacheStorage::LazyInitDone() {
626 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
627 it != init_callbacks_.end();
631 init_callbacks_.clear();
634 void ServiceWorkerCacheStorage::AddCacheToMaps(
635 scoped_ptr<ServiceWorkerCache> cache) {
636 DCHECK_CURRENTLY_ON(BrowserThread::IO);
638 ServiceWorkerCache* cache_ptr = cache.release();
639 CacheID id = cache_map_.Add(cache_ptr);
640 name_map_.insert(std::make_pair(cache_ptr->name(), id));
641 cache_ptr->set_id(id);
644 void ServiceWorkerCacheStorage::CreateCacheDidCreateCache(
645 const std::string& cache_name,
646 const CacheAndErrorCallback& callback,
647 scoped_ptr<ServiceWorkerCache> cache) {
648 DCHECK_CURRENTLY_ON(BrowserThread::IO);
651 callback.Run(0, CACHE_STORAGE_ERROR_STORAGE);
655 base::WeakPtr<ServiceWorkerCache> cache_ptr = cache->AsWeakPtr();
657 AddCacheToMaps(cache.Pass());
659 cache_loader_->WriteIndex(
662 &ServiceWorkerCacheStorage::CreateCacheDidWriteIndex,
663 weak_factory_.GetWeakPtr(),
665 cache_ptr)); // cache is owned by this->CacheMap and won't be deleted
668 void ServiceWorkerCacheStorage::CreateCacheDidWriteIndex(
669 const CacheAndErrorCallback& callback,
670 base::WeakPtr<ServiceWorkerCache> cache,
672 DCHECK_CURRENTLY_ON(BrowserThread::IO);
674 callback.Run(false, CACHE_STORAGE_ERROR_STORAGE);
677 cache->CreateBackend(base::Bind(&ServiceWorkerCacheStorage::DidCreateBackend,
678 weak_factory_.GetWeakPtr(),
683 void ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex(
684 const std::string& cache_name,
685 const BoolAndErrorCallback& callback,
687 DCHECK_CURRENTLY_ON(BrowserThread::IO);
689 cache_loader_->CleanUpDeletedCache(
691 base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidCleanUp,
692 weak_factory_.GetWeakPtr(),
696 void ServiceWorkerCacheStorage::DeleteCacheDidCleanUp(
697 const BoolAndErrorCallback& callback,
699 DCHECK_CURRENTLY_ON(BrowserThread::IO);
701 callback.Run(true, CACHE_STORAGE_ERROR_NO_ERROR);
704 ServiceWorkerCache* ServiceWorkerCacheStorage::GetLoadedCache(
705 const std::string& cache_name) const {
706 DCHECK_CURRENTLY_ON(BrowserThread::IO);
707 DCHECK(initialized_);
709 NameMap::const_iterator it = name_map_.find(cache_name);
710 if (it == name_map_.end())
713 ServiceWorkerCache* cache = cache_map_.Lookup(it->second);
718 } // namespace content