Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(419)

Unified Diff: content/browser/service_worker/service_worker_fetch_stores.cc

Issue 424863002: Fill in some of ServiceWorkerFetchStores. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@cache_storage
Patch Set: Asynchronous cache init Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..835cf8b0621068c80e25205612c01a22cbd7c2bd 100644
--- a/content/browser/service_worker/service_worker_fetch_stores.cc
+++ b/content/browser/service_worker/service_worker_fetch_stores.cc
@@ -6,17 +6,148 @@
#include <string>
+#include "base/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/pickle.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_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 {
+ scoped_ptr<Pickle> pickle(new Pickle());
michaeln 2014/08/07 00:10:13 We prefer to use formats on disk that are endian a
jkarlin 2014/08/07 16:02:35 Done. Note I renamed the protobuf target from 'ser
+
+ for (StoreMap::const_iterator iter(stores); !iter.IsAtEnd();
+ iter.Advance()) {
+ const ServiceWorkerFetchStore* store = iter.GetCurrentValue();
+ pickle->WriteString(store->name());
+ }
+
+ 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, static_cast<const char*>(pickle->data()), pickle->size());
+ if (bytes_written != implicit_cast<int>(pickle->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");
+ base::MemoryMappedFile index_file_map;
michaeln 2014/08/07 00:10:13 memmapped files seems like an odd choice here, is
jkarlin 2014/08/07 16:02:35 Yes, ReadFileToString is better. Thanks. Done.
+ if (!index_file_map.Initialize(index_path)) {
+ base::DeleteFile(index_path, /* recursive */ false);
+ return;
+ }
+
+ Pickle pickle(reinterpret_cast<const char*>(index_file_map.data()),
+ index_file_map.length());
+ if (!pickle.data())
+ return;
+
+ PickleIterator pickle_it(pickle);
+ std::string name;
+ while (pickle.ReadString(&pickle_it, &name))
+ names->push_back(name);
+ return;
+
+ // TODO(jkarlin): Delete stores that are in the directory and not returned
+ // in LoadIndex.
+ }
+
+ private:
+ std::string HexedHash(const std::string& value) {
+ std::string value_hash = base::SHA1HashString(value);
+ return StringToLowerASCII(
+ base::HexEncode(value_hash.c_str(), value_hash.length()));
+ }
+
+ 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() {
@@ -25,47 +156,174 @@ ServiceWorkerFetchStores::~ServiceWorkerFetchStores() {
void ServiceWorkerFetchStores::CreateStore(
const std::string& key,
const StoreAndErrorCallback& callback) {
- // TODO(jkarlin): Implement this.
+ LazyInit();
+
+ if (key.empty()) {
+ callback_loop_->PostTask(
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY));
+ return;
+ }
+
+ if (GetLoadedStore(key)) {
+ callback_loop_->PostTask(
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS));
+ return;
+ }
+
+ ServiceWorkerFetchStore* store = stores_loader_->CreateStore(key);
+
+ if (!store) {
+ callback_loop_->PostTask(
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_STORAGE));
+ return;
+ }
+
+ InitStore(store);
- callback_loop_->PostTask(FROM_HERE,
- base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS));
- return;
+ stores_loader_->WriteIndex(&store_map_);
+
+ store->InitializeIfNeeded(
+ base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback,
+ base::Unretained(this),
+ store,
+ callback));
+
+ callback_loop_->PostTask(
michaeln 2014/08/07 00:10:13 i think this is an extra call to 'callback' that p
jkarlin 2014/08/07 16:02:35 Done.
+ FROM_HERE,
+ base::Bind(callback, store->id(), FETCH_STORES_ERROR_NO_ERROR));
}
void ServiceWorkerFetchStores::Get(const std::string& key,
michaeln 2014/08/07 00:10:13 Another thing that could help with Store vs Stores
jkarlin 2014/08/07 16:02:35 I renamed key -> store_name in the stores* classes
const StoreAndErrorCallback& callback) {
- // TODO(jkarlin): Implement this.
+ LazyInit();
+
+ if (key.empty()) {
+ callback_loop_->PostTask(
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY));
+ return;
+ }
- callback_loop_->PostTask(FROM_HERE,
- base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS));
- return;
+ ServiceWorkerFetchStore* store = GetLoadedStore(key);
+ if (!store) {
+ callback_loop_->PostTask(
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_NOT_FOUND));
+ return;
+ }
+
+ store->InitializeIfNeeded(
+ base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback,
+ base::Unretained(this),
+ store,
+ callback));
}
void ServiceWorkerFetchStores::Has(const std::string& key,
- const BoolAndErrorCallback& callback) const {
- // TODO(jkarlin): Implement this.
+ const BoolAndErrorCallback& callback) {
+ LazyInit();
+
+ if (key.empty()) {
+ callback_loop_->PostTask(
+ FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY));
+ return;
+ }
callback_loop_->PostTask(
- FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EXISTS));
- return;
+ FROM_HERE,
+ base::Bind(callback, GetLoadedStore(key), FETCH_STORES_ERROR_NO_ERROR));
}
void ServiceWorkerFetchStores::Delete(const std::string& key,
- const StoreAndErrorCallback& callback) {
- // TODO(jkarlin): Implement this.
+ const BoolAndErrorCallback& callback) {
+ LazyInit();
+
+ if (key.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(key);
+ if (!store) {
+ callback_loop_->PostTask(
+ FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_NOT_FOUND));
+ return;
+ }
+
+ name_map_.erase(key);
+ store_map_.Remove(store->id()); // deletes store
+
+ stores_loader_->WriteIndex(&store_map_); // Update the index.
+
+ stores_loader_->CleanUpDeletedStore(key);
+
+ 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::Keys(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

Powered by Google App Engine
This is Rietveld 408576698