Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_cache_storage.cc
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
23 namespace 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.
28 class 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.
77 class 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
127 class 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
332 ServiceWorkerCacheStorage::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
351 ServiceWorkerCacheStorage::~ServiceWorkerCacheStorage() {
352 }
353
354 void 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
379 void 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
402 void 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
419 void 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
459 void 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.
474 void 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
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);
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
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);
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
543 void 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
553 void 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
566 void 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
574 scoped_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