Index: content/browser/service_worker/service_worker_fetch_stores.cc |
diff --git a/content/browser/service_worker/service_worker_fetch_stores.cc b/content/browser/service_worker/service_worker_fetch_stores.cc |
index 643ed4493953533f54d5e43c06b3a1ded40013ea..7f818398ca37083195d4a5ab6096f63ea773dad4 100644 |
--- a/content/browser/service_worker/service_worker_fetch_stores.cc |
+++ b/content/browser/service_worker/service_worker_fetch_stores.cc |
@@ -6,66 +6,329 @@ |
#include <string> |
+#include "base/file_util.h" |
+#include "base/files/memory_mapped_file.h" |
+#include "base/sha1.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_util.h" |
+#include "content/browser/service_worker/service_worker_cache.pb.h" |
+#include "content/browser/service_worker/service_worker_fetch_store.h" |
#include "content/public/browser/browser_thread.h" |
+#include "net/base/directory_lister.h" |
namespace content { |
+// Handles the loading of ServiceWorkerFetchStore and any extra clean up other |
+// than deleting the ServiceWorkerFetchStore object. |
+class ServiceWorkerFetchStores::StoresLoader { |
+ public: |
+ virtual ~StoresLoader() {}; |
+ virtual ServiceWorkerFetchStore* LoadStore(const std::string& key) = 0; |
+ // Creates a new store, deleting any pre-existing store of the same name. |
+ virtual ServiceWorkerFetchStore* CreateStore(const std::string& key) = 0; |
+ virtual bool CleanUpDeletedStore(const std::string& key) = 0; |
+ virtual bool WriteIndex(StoreMap* stores) = 0; |
+ virtual void LoadIndex(std::vector<std::string>* names) = 0; |
+}; |
+ |
+class ServiceWorkerFetchStores::MemoryLoader |
+ : public ServiceWorkerFetchStores::StoresLoader { |
+ public: |
+ virtual content::ServiceWorkerFetchStore* LoadStore( |
+ const std::string& key) OVERRIDE { |
+ NOTREACHED(); |
+ return NULL; |
+ } |
+ |
+ virtual ServiceWorkerFetchStore* CreateStore( |
+ const std::string& key) OVERRIDE { |
+ return ServiceWorkerFetchStore::CreateMemoryStore(key); |
+ } |
+ |
+ virtual bool CleanUpDeletedStore(const std::string& key) OVERRIDE { |
+ return true; |
+ } |
+ |
+ virtual bool WriteIndex(StoreMap* stores) OVERRIDE { return false; } |
+ |
+ virtual void LoadIndex(std::vector<std::string>* names) OVERRIDE { return; } |
+}; |
+ |
+class ServiceWorkerFetchStores::SimpleCacheLoader |
+ : public ServiceWorkerFetchStores::StoresLoader { |
+ public: |
+ explicit SimpleCacheLoader(const base::FilePath& origin_path) |
+ : origin_path_(origin_path) {} |
+ |
+ virtual ServiceWorkerFetchStore* LoadStore(const std::string& key) OVERRIDE { |
+ base::CreateDirectory(CreatePersistentStorePath(origin_path_, key)); |
+ return ServiceWorkerFetchStore::CreatePersistentStore( |
+ CreatePersistentStorePath(origin_path_, key), key); |
+ } |
+ |
+ virtual ServiceWorkerFetchStore* CreateStore( |
+ const std::string& key) OVERRIDE { |
+ base::FilePath store_path = CreatePersistentStorePath(origin_path_, key); |
+ if (base::PathExists(store_path)) |
+ base::DeleteFile(store_path, /* recursive */ true); |
+ return LoadStore(key); |
+ } |
+ |
+ virtual bool CleanUpDeletedStore(const std::string& key) OVERRIDE { |
+ return base::DeleteFile(CreatePersistentStorePath(origin_path_, key), true); |
+ } |
+ |
+ virtual bool WriteIndex(StoreMap* stores) OVERRIDE { |
+ ServiceWorkerCacheStorageIndex index; |
+ |
+ for (StoreMap::const_iterator iter(stores); !iter.IsAtEnd(); |
+ iter.Advance()) { |
+ const ServiceWorkerFetchStore* store = iter.GetCurrentValue(); |
+ ServiceWorkerCacheStorageIndex::Cache* cache = index.add_cache(); |
+ cache->set_name(store->name()); |
+ cache->set_size(0); // TODO(jkarlin): Make this real. |
+ } |
+ |
+ std::string serialized; |
+ bool success = index.SerializeToString(&serialized); |
+ DCHECK(success); |
+ |
+ base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp"); |
+ base::FilePath index_path = origin_path_.AppendASCII("index.txt"); |
+ |
+ int bytes_written = |
+ base::WriteFile(tmp_path, serialized.c_str(), serialized.size()); |
+ if (bytes_written != implicit_cast<int>(serialized.size())) { |
+ base::DeleteFile(tmp_path, /* recursive */ false); |
+ return false; |
+ } |
+ |
+ // Atomically rename the temporary index file to become the real one. |
+ return base::ReplaceFile(tmp_path, index_path, NULL); |
+ } |
+ |
+ virtual void LoadIndex(std::vector<std::string>* names) OVERRIDE { |
+ base::FilePath index_path = origin_path_.AppendASCII("index.txt"); |
+ |
+ std::string serialized; |
+ if (!base::ReadFileToString(index_path, &serialized)) |
+ return; |
+ |
+ ServiceWorkerCacheStorageIndex index; |
+ index.ParseFromString(serialized); |
+ |
+ for (int i = 0, max = index.cache_size(); i < max; ++i) { |
+ const ServiceWorkerCacheStorageIndex::Cache& cache = index.cache(i); |
+ names->push_back(cache.name()); |
+ } |
+ |
+ // TODO(jkarlin): Delete stores that are in the directory and not returned |
+ // in LoadIndex. |
+ return; |
+ } |
+ |
+ private: |
+ std::string HexedHash(const std::string& value) { |
+ std::string value_hash = base::SHA1HashString(value); |
+ std::string valued_hexed_hash = base::StringToLowerASCII( |
+ base::HexEncode(value_hash.c_str(), value_hash.length())); |
+ return valued_hexed_hash; |
+ } |
+ |
+ base::FilePath CreatePersistentStorePath(const base::FilePath& origin_path, |
+ const std::string& store_name) { |
+ return origin_path.AppendASCII(HexedHash(store_name)); |
+ } |
+ |
+ const base::FilePath origin_path_; |
+}; |
+ |
ServiceWorkerFetchStores::ServiceWorkerFetchStores( |
const base::FilePath& path, |
- BackendType backend_type, |
+ bool memory_only, |
const scoped_refptr<base::MessageLoopProxy>& callback_loop) |
- : origin_path_(path), |
- backend_type_(backend_type), |
- callback_loop_(callback_loop) { |
+ : initialized_(false), origin_path_(path), callback_loop_(callback_loop) { |
+ if (memory_only) |
+ stores_loader_.reset(new MemoryLoader()); |
+ else |
+ stores_loader_.reset(new SimpleCacheLoader(origin_path_)); |
} |
ServiceWorkerFetchStores::~ServiceWorkerFetchStores() { |
} |
void ServiceWorkerFetchStores::CreateStore( |
- const std::string& key, |
+ const std::string& store_name, |
const StoreAndErrorCallback& callback) { |
- // TODO(jkarlin): Implement this. |
+ LazyInit(); |
+ |
+ if (store_name.empty()) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY)); |
+ return; |
+ } |
+ |
+ if (GetLoadedStore(store_name)) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); |
+ return; |
+ } |
+ |
+ ServiceWorkerFetchStore* store = stores_loader_->CreateStore(store_name); |
+ |
+ if (!store) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_STORAGE)); |
+ return; |
+ } |
- callback_loop_->PostTask(FROM_HERE, |
- base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); |
- return; |
+ InitStore(store); |
+ |
+ stores_loader_->WriteIndex(&store_map_); |
+ |
+ store->CreateBackend( |
+ base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback, |
+ base::Unretained(this), |
+ store, |
+ callback)); |
} |
-void ServiceWorkerFetchStores::Get(const std::string& key, |
- const StoreAndErrorCallback& callback) { |
- // TODO(jkarlin): Implement this. |
+void ServiceWorkerFetchStores::GetStore(const std::string& store_name, |
+ const StoreAndErrorCallback& callback) { |
+ LazyInit(); |
+ |
+ if (store_name.empty()) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY)); |
+ return; |
+ } |
+ |
+ ServiceWorkerFetchStore* store = GetLoadedStore(store_name); |
+ if (!store) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_NOT_FOUND)); |
+ return; |
+ } |
- callback_loop_->PostTask(FROM_HERE, |
- base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); |
- return; |
+ store->CreateBackend( |
+ base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback, |
+ base::Unretained(this), |
+ store, |
+ callback)); |
} |
-void ServiceWorkerFetchStores::Has(const std::string& key, |
- const BoolAndErrorCallback& callback) const { |
- // TODO(jkarlin): Implement this. |
+void ServiceWorkerFetchStores::HasStore(const std::string& store_name, |
+ const BoolAndErrorCallback& callback) { |
+ LazyInit(); |
+ |
+ if (store_name.empty()) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EMPTY_KEY)); |
+ return; |
+ } |
+ |
+ bool has_store = GetLoadedStore(store_name) != NULL; |
callback_loop_->PostTask( |
- FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EXISTS)); |
- return; |
+ FROM_HERE, |
+ base::Bind( |
+ callback, has_store, FETCH_STORES_ERROR_NO_ERROR)); |
} |
-void ServiceWorkerFetchStores::Delete(const std::string& key, |
- const StoreAndErrorCallback& callback) { |
- // TODO(jkarlin): Implement this. |
+void ServiceWorkerFetchStores::DeleteStore( |
+ const std::string& store_name, |
+ const BoolAndErrorCallback& callback) { |
+ LazyInit(); |
+ |
+ if (store_name.empty()) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EMPTY_KEY)); |
+ return; |
+ } |
- callback_loop_->PostTask(FROM_HERE, |
- base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); |
- return; |
+ ServiceWorkerFetchStore* store = GetLoadedStore(store_name); |
+ if (!store) { |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_NOT_FOUND)); |
+ return; |
+ } |
+ |
+ name_map_.erase(store_name); |
+ store_map_.Remove(store->id()); // deletes store |
+ |
+ stores_loader_->WriteIndex(&store_map_); // Update the index. |
+ |
+ stores_loader_->CleanUpDeletedStore(store_name); |
+ |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, true, FETCH_STORES_ERROR_NO_ERROR)); |
} |
-void ServiceWorkerFetchStores::Keys( |
- const StringsAndErrorCallback& callback) const { |
- // TODO(jkarlin): Implement this. |
- std::vector<std::string> out; |
+void ServiceWorkerFetchStores::EnumerateStores( |
+ const StringsAndErrorCallback& callback) { |
+ LazyInit(); |
+ |
+ std::vector<std::string> names; |
+ for (NameMap::const_iterator it = name_map_.begin(); it != name_map_.end(); |
+ ++it) { |
+ names.push_back(it->first); |
+ } |
+ |
callback_loop_->PostTask( |
- FROM_HERE, base::Bind(callback, out, FETCH_STORES_ERROR_EXISTS)); |
- return; |
+ FROM_HERE, base::Bind(callback, names, FETCH_STORES_ERROR_NO_ERROR)); |
+} |
+ |
+void ServiceWorkerFetchStores::InitializeStoreCallback( |
+ const ServiceWorkerFetchStore* store, |
+ const StoreAndErrorCallback& callback, |
+ bool success) { |
+ if (!success) { |
+ // TODO(jkarlin): This should delete the directory and try again in case |
+ // the cache is simply corrupt. |
+ callback_loop_->PostTask( |
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_STORAGE)); |
+ return; |
+ } |
+ callback_loop_->PostTask( |
+ FROM_HERE, |
+ base::Bind(callback, store->id(), FETCH_STORES_ERROR_NO_ERROR)); |
+} |
+ |
+// Init is run lazily so that it is called on the proper MessageLoop. |
+void ServiceWorkerFetchStores::LazyInit() { |
+ if (initialized_) |
+ return; |
+ |
+ std::vector<std::string> indexed_store_names; |
+ stores_loader_->LoadIndex(&indexed_store_names); |
+ |
+ for (std::vector<std::string>::const_iterator it = |
+ indexed_store_names.begin(); |
+ it != indexed_store_names.end(); |
+ ++it) { |
+ ServiceWorkerFetchStore* store = stores_loader_->LoadStore(*it); |
+ InitStore(store); |
+ } |
+ initialized_ = true; |
+} |
+ |
+void ServiceWorkerFetchStores::InitStore(ServiceWorkerFetchStore* store) { |
+ StoreID id = store_map_.Add(store); |
+ name_map_.insert(std::make_pair(store->name(), id)); |
+ store->set_id(id); |
+} |
+ |
+ServiceWorkerFetchStore* ServiceWorkerFetchStores::GetLoadedStore( |
+ const std::string& key) const { |
+ DCHECK(initialized_); |
+ |
+ NameMap::const_iterator it = name_map_.find(key); |
+ if (it == name_map_.end()) |
+ return NULL; |
+ |
+ ServiceWorkerFetchStore* store = store_map_.Lookup(it->second); |
+ DCHECK(store); |
+ return store; |
} |
} // namespace content |