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.
4
5#include "content/browser/service_worker/service_worker_cache_storage.h"
6
7#include <string>
8
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"
22
23namespace content {
24
25
26// Handles the loading and clean up of ServiceWorkerCache objects. The
27// callback of every public method is guaranteed to be called.
28class ServiceWorkerCacheStorage::CacheLoader {
29 public:
30  typedef base::Callback<void(const scoped_refptr<ServiceWorkerCache>&)>
31      CacheCallback;
32  typedef base::Callback<void(bool)> BoolCallback;
33  typedef base::Callback<void(scoped_ptr<std::vector<std::string> >)>
34      StringVectorCallback;
35
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) {}
42
43  virtual ~CacheLoader() {}
44
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;
49
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;
53
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;
58
59  // Writes the cache names (and sizes) to disk if applicable.
60  virtual void WriteIndex(const StringVector& cache_names,
61                          const BoolCallback& callback) = 0;
62
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;
66
67 protected:
68  scoped_refptr<base::SequencedTaskRunner> cache_task_runner_;
69  net::URLRequestContext* request_context_;
70  base::WeakPtr<storage::BlobStorageContext> blob_context_;
71};
72
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
76// cache is deleted.
77class ServiceWorkerCacheStorage::MemoryLoader
78    : public ServiceWorkerCacheStorage::CacheLoader {
79 public:
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) {}
84
85  virtual scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
86      const std::string& cache_name) OVERRIDE {
87    return ServiceWorkerCache::CreateMemoryCache(request_context_,
88                                                 blob_context_);
89  }
90
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));
96    callback.Run(cache);
97  }
98
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);
104    callback.Run(true);
105  }
106
107  virtual void WriteIndex(const StringVector& cache_names,
108                          const BoolCallback& callback) OVERRIDE {
109    callback.Run(false);
110  }
111
112  virtual void LoadIndex(scoped_ptr<std::vector<std::string> > cache_names,
113                         const StringVectorCallback& callback) OVERRIDE {
114    callback.Run(cache_names.Pass());
115  }
116
117 private:
118  typedef std::map<std::string, scoped_refptr<ServiceWorkerCache> > CacheRefMap;
119  virtual ~MemoryLoader() {}
120
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
123  // freed.
124  CacheRefMap cache_refs_;
125};
126
127class ServiceWorkerCacheStorage::SimpleCacheLoader
128    : public ServiceWorkerCacheStorage::CacheLoader {
129 public:
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) {}
137
138  virtual scoped_refptr<ServiceWorkerCache> CreateServiceWorkerCache(
139      const std::string& cache_name) OVERRIDE {
140    DCHECK_CURRENTLY_ON(BrowserThread::IO);
141
142    return ServiceWorkerCache::CreatePersistentCache(
143        CreatePersistentCachePath(origin_path_, cache_name),
144        request_context_,
145        blob_context_);
146  }
147
148  virtual void CreateCache(const std::string& cache_name,
149                           const CacheCallback& callback) OVERRIDE {
150    DCHECK_CURRENTLY_ON(BrowserThread::IO);
151
152    // 1. Delete the cache's directory if it exists.
153    // (CreateCacheDeleteFilesInPool)
154    // 2. Load the cache. (LoadCreateDirectoryInPool)
155
156    base::FilePath cache_path =
157        CreatePersistentCachePath(origin_path_, cache_name);
158
159    PostTaskAndReplyWithResult(
160        cache_task_runner_.get(),
161        FROM_HERE,
162        base::Bind(&SimpleCacheLoader::CreateCachePrepDirInPool, cache_path),
163        base::Bind(&SimpleCacheLoader::CreateCachePreppedDir,
164                   cache_name,
165                   callback,
166                   weak_ptr_factory_.GetWeakPtr()));
167  }
168
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);
173  }
174
175  static void CreateCachePreppedDir(const std::string& cache_name,
176                                    const CacheCallback& callback,
177                                    base::WeakPtr<SimpleCacheLoader> loader,
178                                    bool success) {
179    if (!success || !loader) {
180      callback.Run(scoped_refptr<ServiceWorkerCache>());
181      return;
182    }
183
184    callback.Run(loader->CreateServiceWorkerCache(cache_name));
185  }
186
187  virtual void CleanUpDeletedCache(const std::string& cache_name,
188                                   const BoolCallback& callback) OVERRIDE {
189    DCHECK_CURRENTLY_ON(BrowserThread::IO);
190
191    // 1. Delete the cache's directory. (CleanUpDeleteCacheDirInPool)
192
193    base::FilePath cache_path =
194        CreatePersistentCachePath(origin_path_, cache_name);
195    cache_task_runner_->PostTask(
196        FROM_HERE,
197        base::Bind(&SimpleCacheLoader::CleanUpDeleteCacheDirInPool,
198                   cache_path,
199                   callback,
200                   base::MessageLoopProxy::current()));
201  }
202
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));
209  }
210
211  virtual void WriteIndex(const StringVector& cache_names,
212                          const BoolCallback& callback) OVERRIDE {
213    DCHECK_CURRENTLY_ON(BrowserThread::IO);
214
215    // 1. Create the index file as a string. (WriteIndex)
216    // 2. Write the file to disk. (WriteIndexWriteToFileInPool)
217
218    ServiceWorkerCacheStorageIndex index;
219
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.
224    }
225
226    std::string serialized;
227    bool success = index.SerializeToString(&serialized);
228    DCHECK(success);
229
230    base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp");
231    base::FilePath index_path = origin_path_.AppendASCII("index.txt");
232
233    cache_task_runner_->PostTask(
234        FROM_HERE,
235        base::Bind(&SimpleCacheLoader::WriteIndexWriteToFileInPool,
236                   tmp_path,
237                   index_path,
238                   serialized,
239                   callback,
240                   base::MessageLoopProxy::current()));
241  }
242
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));
253    }
254
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));
258  }
259
260  virtual void LoadIndex(scoped_ptr<std::vector<std::string> > names,
261                         const StringVectorCallback& callback) OVERRIDE {
262    DCHECK_CURRENTLY_ON(BrowserThread::IO);
263
264    // 1. Read the file from disk. (LoadIndexReadFileInPool)
265    // 2. Parse file and return the names of the caches (LoadIndexDidReadFile)
266
267    base::FilePath index_path = origin_path_.AppendASCII("index.txt");
268
269    cache_task_runner_->PostTask(
270        FROM_HERE,
271        base::Bind(&SimpleCacheLoader::LoadIndexReadFileInPool,
272                   index_path,
273                   base::Passed(names.Pass()),
274                   callback,
275                   base::MessageLoopProxy::current()));
276  }
277
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) {
283    std::string body;
284    base::ReadFileToString(index_path, &body);
285
286    original_loop->PostTask(FROM_HERE,
287                            base::Bind(&SimpleCacheLoader::LoadIndexDidReadFile,
288                                       base::Passed(names.Pass()),
289                                       callback,
290                                       body));
291  }
292
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);
297
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());
303      }
304    }
305
306    // TODO(jkarlin): Delete caches that are in the directory and not returned
307    // in LoadIndex.
308    callback.Run(names.Pass());
309  }
310
311 private:
312  virtual ~SimpleCacheLoader() {}
313
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;
319  }
320
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));
325  }
326
327  const base::FilePath origin_path_;
328
329  base::WeakPtrFactory<SimpleCacheLoader> weak_ptr_factory_;
330};
331
332ServiceWorkerCacheStorage::ServiceWorkerCacheStorage(
333    const base::FilePath& path,
334    bool memory_only,
335    base::SequencedTaskRunner* cache_task_runner,
336    net::URLRequestContext* request_context,
337    base::WeakPtr<storage::BlobStorageContext> blob_context)
338    : initialized_(false),
339      origin_path_(path),
340      cache_task_runner_(cache_task_runner),
341      memory_only_(memory_only),
342      weak_factory_(this) {
343  if (memory_only)
344    cache_loader_.reset(new MemoryLoader(
345        cache_task_runner_.get(), request_context, blob_context));
346  else
347    cache_loader_.reset(new SimpleCacheLoader(
348        origin_path_, cache_task_runner_.get(), request_context, blob_context));
349}
350
351ServiceWorkerCacheStorage::~ServiceWorkerCacheStorage() {
352}
353
354void ServiceWorkerCacheStorage::CreateCache(
355    const std::string& cache_name,
356    const CacheAndErrorCallback& callback) {
357  if (!initialized_) {
358    LazyInit(base::Bind(&ServiceWorkerCacheStorage::CreateCache,
359                        weak_factory_.GetWeakPtr(),
360                        cache_name,
361                        callback));
362    return;
363  }
364
365  if (cache_map_.find(cache_name) != cache_map_.end()) {
366    callback.Run(scoped_refptr<ServiceWorkerCache>(),
367                 CACHE_STORAGE_ERROR_EXISTS);
368    return;
369  }
370
371  cache_loader_->CreateCache(
372      cache_name,
373      base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidCreateCache,
374                 weak_factory_.GetWeakPtr(),
375                 cache_name,
376                 callback));
377}
378
379void ServiceWorkerCacheStorage::GetCache(
380    const std::string& cache_name,
381    const CacheAndErrorCallback& callback) {
382  DCHECK_CURRENTLY_ON(BrowserThread::IO);
383
384  if (!initialized_) {
385    LazyInit(base::Bind(&ServiceWorkerCacheStorage::GetCache,
386                        weak_factory_.GetWeakPtr(),
387                        cache_name,
388                        callback));
389    return;
390  }
391
392  scoped_refptr<ServiceWorkerCache> cache = GetLoadedCache(cache_name);
393  if (!cache.get()) {
394    callback.Run(scoped_refptr<ServiceWorkerCache>(),
395                 CACHE_STORAGE_ERROR_NOT_FOUND);
396    return;
397  }
398
399  callback.Run(cache, CACHE_STORAGE_ERROR_NO_ERROR);
400}
401
402void ServiceWorkerCacheStorage::HasCache(const std::string& cache_name,
403                                         const BoolAndErrorCallback& callback) {
404  DCHECK_CURRENTLY_ON(BrowserThread::IO);
405
406  if (!initialized_) {
407    LazyInit(base::Bind(&ServiceWorkerCacheStorage::HasCache,
408                        weak_factory_.GetWeakPtr(),
409                        cache_name,
410                        callback));
411    return;
412  }
413
414  bool has_cache = cache_map_.find(cache_name) != cache_map_.end();
415
416  callback.Run(has_cache, CACHE_STORAGE_ERROR_NO_ERROR);
417}
418
419void ServiceWorkerCacheStorage::DeleteCache(
420    const std::string& cache_name,
421    const BoolAndErrorCallback& callback) {
422  DCHECK_CURRENTLY_ON(BrowserThread::IO);
423
424  if (!initialized_) {
425    LazyInit(base::Bind(&ServiceWorkerCacheStorage::DeleteCache,
426                        weak_factory_.GetWeakPtr(),
427                        cache_name,
428                        callback));
429    return;
430  }
431
432  CacheMap::iterator it = cache_map_.find(cache_name);
433  if (it == cache_map_.end()) {
434    callback.Run(false, CACHE_STORAGE_ERROR_NOT_FOUND);
435    return;
436  }
437
438  base::WeakPtr<ServiceWorkerCache> cache = it->second;
439  if (cache)
440    cache->Close();
441
442  cache_map_.erase(it);
443
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);
449
450  // Update the Index
451  cache_loader_->WriteIndex(
452      ordered_cache_names_,
453      base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex,
454                 weak_factory_.GetWeakPtr(),
455                 cache_name,
456                 callback));
457}
458
459void ServiceWorkerCacheStorage::EnumerateCaches(
460    const StringsAndErrorCallback& callback) {
461  DCHECK_CURRENTLY_ON(BrowserThread::IO);
462
463  if (!initialized_) {
464    LazyInit(base::Bind(&ServiceWorkerCacheStorage::EnumerateCaches,
465                        weak_factory_.GetWeakPtr(),
466                        callback));
467    return;
468  }
469
470  callback.Run(ordered_cache_names_, CACHE_STORAGE_ERROR_NO_ERROR);
471}
472
473// Init is run lazily so that it is called on the proper MessageLoop.
474void ServiceWorkerCacheStorage::LazyInit(const base::Closure& callback) {
475  DCHECK_CURRENTLY_ON(BrowserThread::IO);
476  DCHECK(!initialized_);
477
478  init_callbacks_.push_back(callback);
479
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)
483    return;
484
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.
489
490  scoped_ptr<std::vector<std::string> > indexed_cache_names(
491      new std::vector<std::string>());
492
493  cache_loader_->LoadIndex(
494      indexed_cache_names.Pass(),
495      base::Bind(&ServiceWorkerCacheStorage::LazyInitDidLoadIndex,
496                 weak_factory_.GetWeakPtr(),
497                 callback));
498}
499
500void ServiceWorkerCacheStorage::LazyInitDidLoadIndex(
501    const base::Closure& callback,
502    scoped_ptr<std::vector<std::string> > indexed_cache_names) {
503  DCHECK_CURRENTLY_ON(BrowserThread::IO);
504
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));
509  }
510
511  initialized_ = true;
512  for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
513       it != init_callbacks_.end();
514       ++it) {
515    it->Run();
516  }
517  init_callbacks_.clear();
518}
519
520void ServiceWorkerCacheStorage::CreateCacheDidCreateCache(
521    const std::string& cache_name,
522    const CacheAndErrorCallback& callback,
523    const scoped_refptr<ServiceWorkerCache>& cache) {
524  DCHECK_CURRENTLY_ON(BrowserThread::IO);
525
526  if (!cache.get()) {
527    callback.Run(scoped_refptr<ServiceWorkerCache>(),
528                 CACHE_STORAGE_ERROR_CLOSING);
529    return;
530  }
531
532  cache_map_.insert(std::make_pair(cache_name, cache->AsWeakPtr()));
533  ordered_cache_names_.push_back(cache_name);
534
535  cache_loader_->WriteIndex(
536      ordered_cache_names_,
537      base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidWriteIndex,
538                 weak_factory_.GetWeakPtr(),
539                 callback,
540                 cache));
541}
542
543void ServiceWorkerCacheStorage::CreateCacheDidWriteIndex(
544    const CacheAndErrorCallback& callback,
545    const scoped_refptr<ServiceWorkerCache>& cache,
546    bool success) {
547  DCHECK_CURRENTLY_ON(BrowserThread::IO);
548  DCHECK(cache.get());
549
550  callback.Run(cache, CACHE_STORAGE_ERROR_NO_ERROR);
551}
552
553void ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex(
554    const std::string& cache_name,
555    const BoolAndErrorCallback& callback,
556    bool success) {
557  DCHECK_CURRENTLY_ON(BrowserThread::IO);
558
559  cache_loader_->CleanUpDeletedCache(
560      cache_name,
561      base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidCleanUp,
562                 weak_factory_.GetWeakPtr(),
563                 callback));
564}
565
566void ServiceWorkerCacheStorage::DeleteCacheDidCleanUp(
567    const BoolAndErrorCallback& callback,
568    bool success) {
569  DCHECK_CURRENTLY_ON(BrowserThread::IO);
570
571  callback.Run(true, CACHE_STORAGE_ERROR_NO_ERROR);
572}
573
574scoped_refptr<ServiceWorkerCache> ServiceWorkerCacheStorage::GetLoadedCache(
575    const std::string& cache_name) {
576  DCHECK_CURRENTLY_ON(BrowserThread::IO);
577  DCHECK(initialized_);
578
579  CacheMap::iterator map_iter = cache_map_.find(cache_name);
580  if (map_iter == cache_map_.end())
581    return scoped_refptr<ServiceWorkerCache>();
582
583  base::WeakPtr<ServiceWorkerCache> cache = map_iter->second;
584
585  if (!cache) {
586    scoped_refptr<ServiceWorkerCache> new_cache =
587        cache_loader_->CreateServiceWorkerCache(cache_name);
588    map_iter->second = new_cache->AsWeakPtr();
589    return new_cache;
590  }
591
592  return make_scoped_refptr(cache.get());
593}
594
595}  // namespace content
596