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