Commit acb4e9e2 authored by jkarlin@chromium.org's avatar jkarlin@chromium.org

Fills in ServiceWorkerFetchStores. Adds a stub ServiceWorkerFetchStore class...

Fills in ServiceWorkerFetchStores.  Adds a stub ServiceWorkerFetchStore class which will need to be filled in. Also adds some tests.

A CL to refactor ServiceWorkerFetchStores -> ServiceWorkerCacheStorage is upcoming.

BUG=392621

Review URL: https://codereview.chromium.org/424863002

Cr-Commit-Position: refs/heads/master@{#288379}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288379 0039d316-1c4b-4281-b951-d872f2087c98
parent 8c271870
......@@ -24,7 +24,7 @@ source_set("browser") {
deps = [
"//base",
"//content:resources",
"//content/browser/service_worker:database_proto",
"//content/browser/service_worker:proto",
"//content/browser/speech/proto",
"//crypto",
"//google_apis",
......
......@@ -4,8 +4,9 @@
import("//third_party/protobuf/proto_library.gni")
proto_library("database_proto") {
proto_library("proto") {
sources = [
"service_worker_cache.proto",
"service_worker_database.proto",
]
}
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package content;
message ServiceWorkerCacheStorageIndex {
message Cache {
required string name = 1;
required int32 size = 2;
}
repeated Cache cache = 1;
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/service_worker/service_worker_fetch_store.h"
#include <string>
#include "base/files/file_path.h"
namespace content {
// static
ServiceWorkerFetchStore* ServiceWorkerFetchStore::CreateMemoryStore(
const std::string& name) {
return new ServiceWorkerFetchStore(base::FilePath(), name);
}
// static
ServiceWorkerFetchStore* ServiceWorkerFetchStore::CreatePersistentStore(
const base::FilePath& path,
const std::string& name) {
return new ServiceWorkerFetchStore(path, name);
}
void ServiceWorkerFetchStore::CreateBackend(
const base::Callback<void(bool)>& callback) {
callback.Run(true);
}
ServiceWorkerFetchStore::ServiceWorkerFetchStore(const base::FilePath& path,
const std::string& name)
: path_(path), name_(name), id_(0) {
}
ServiceWorkerFetchStore::~ServiceWorkerFetchStore() {
}
} // namespace content
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORE_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORE_H_
#include "base/callback.h"
#include "base/files/file_path.h"
namespace content {
// TODO(jkarlin): Fill this in with a real FetchStore implementation as
// specified in
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html.
// TODO(jkarlin): Unload store backend from memory once the store object is no
// longer referenced in javascript.
// Represents a ServiceWorker FetchStore as seen in
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html.
// InitializeIfNeeded must be called before calling the other public members.
class ServiceWorkerFetchStore {
public:
static ServiceWorkerFetchStore* CreateMemoryStore(const std::string& name);
static ServiceWorkerFetchStore* CreatePersistentStore(
const base::FilePath& path,
const std::string& name);
virtual ~ServiceWorkerFetchStore();
// Loads the backend and calls the callback with the result (true for
// success). This must be called before member functions that require a
// backend are called.
void CreateBackend(const base::Callback<void(bool)>& callback);
void set_name(const std::string& name) { name_ = name; }
const std::string& name() const { return name_; }
int32 id() const { return id_; }
void set_id(int32 id) { id_ = id; }
private:
ServiceWorkerFetchStore(const base::FilePath& path, const std::string& name);
base::FilePath path_;
std::string name_;
int32 id_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchStore);
};
} // namespace content
#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORE_H_
......@@ -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
......@@ -5,10 +5,12 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_
#include <map>
#include <string>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/id_map.h"
#include "base/threading/thread_checker.h"
namespace base {
......@@ -19,7 +21,8 @@ namespace content {
class ServiceWorkerFetchStore;
// The set of stores for a given ServiceWorker. It is owned by the
// TODO(jkarlin): Constrain the total bytes used per origin.
// The set of stores for a given origin. It is owned by the
// ServiceWorkerFetchStoresManager. Provided callbacks are called on the
// |callback_loop| provided in the constructor.
class ServiceWorkerFetchStores {
......@@ -29,11 +32,10 @@ class ServiceWorkerFetchStores {
FETCH_STORES_ERROR_NOT_IMPLEMENTED,
FETCH_STORES_ERROR_NOT_FOUND,
FETCH_STORES_ERROR_EXISTS,
FETCH_STORES_ERROR_STORAGE
FETCH_STORES_ERROR_STORAGE,
FETCH_STORES_ERROR_EMPTY_KEY,
};
enum BackendType { BACKEND_TYPE_SIMPLE_CACHE, BACKEND_TYPE_MEMORY };
typedef base::Callback<void(bool, FetchStoresError)> BoolAndErrorCallback;
typedef base::Callback<void(int, FetchStoresError)> StoreAndErrorCallback;
typedef base::Callback<void(const std::vector<std::string>&,
......@@ -41,22 +43,60 @@ class ServiceWorkerFetchStores {
ServiceWorkerFetchStores(
const base::FilePath& origin_path,
BackendType backend,
bool memory_only,
const scoped_refptr<base::MessageLoopProxy>& callback_loop);
virtual ~ServiceWorkerFetchStores();
void CreateStore(const std::string& key,
// Create a ServiceWorkerFetchStore if it doesn't already exist and call the
// callback with the cache's id. If it already
// exists the callback is called with FETCH_STORES_ERROR_EXISTS.
void CreateStore(const std::string& store_name,
const StoreAndErrorCallback& callback);
void Get(const std::string& key, const StoreAndErrorCallback& callback);
void Has(const std::string& key, const BoolAndErrorCallback& callback) const;
void Delete(const std::string& key, const StoreAndErrorCallback& callback);
void Keys(const StringsAndErrorCallback& callback) const;
// Get the cache id for the given key. If not found returns
// FETCH_STORES_ERROR_NOT_FOUND.
void GetStore(const std::string& store_name,
const StoreAndErrorCallback& callback);
// Calls the callback with whether or not the cache exists.
void HasStore(const std::string& store_name,
const BoolAndErrorCallback& callback);
// Deletes the cache if it exists. If it doesn't exist,
// FETCH_STORES_ERROR_NOT_FOUND is returned.
void DeleteStore(const std::string& store_name,
const BoolAndErrorCallback& callback);
// Calls the callback with a vector of cache names (keys) available.
void EnumerateStores(const StringsAndErrorCallback& callback);
// TODO(jkarlin): Add match() function.
void InitializeStoreCallback(const ServiceWorkerFetchStore* store,
const StoreAndErrorCallback& callback,
bool success);
private:
class MemoryLoader;
class SimpleCacheLoader;
class StoresLoader;
typedef IDMap<ServiceWorkerFetchStore, IDMapOwnPointer> StoreMap;
typedef StoreMap::KeyType StoreID;
typedef std::map<std::string, StoreID> NameMap;
ServiceWorkerFetchStore* GetLoadedStore(const std::string& key) const;
void LazyInit();
void InitStore(ServiceWorkerFetchStore* store);
bool initialized_;
StoreMap store_map_;
NameMap name_map_;
base::FilePath origin_path_;
BackendType backend_type_;
scoped_refptr<base::MessageLoopProxy> callback_loop_;
scoped_ptr<StoresLoader> stores_loader_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchStores);
};
......
......@@ -65,7 +65,7 @@ ServiceWorkerFetchStoresManager::~ServiceWorkerFetchStoresManager() {
void ServiceWorkerFetchStoresManager::CreateStore(
const GURL& origin,
const std::string& key,
const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
......@@ -76,56 +76,57 @@ void ServiceWorkerFetchStoresManager::CreateStore(
FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::CreateStore,
base::Unretained(stores),
key,
store_name,
callback));
}
void ServiceWorkerFetchStoresManager::Get(
void ServiceWorkerFetchStoresManager::GetStore(
const GURL& origin,
const std::string& key,
const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStores* stores =
FindOrCreateServiceWorkerFetchStores(origin);
stores_task_runner_->PostTask(FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::Get,
base::Bind(&ServiceWorkerFetchStores::GetStore,
base::Unretained(stores),
key,
store_name,
callback));
}
void ServiceWorkerFetchStoresManager::Has(
void ServiceWorkerFetchStoresManager::HasStore(
const GURL& origin,
const std::string& key,
const std::string& store_name,
const ServiceWorkerFetchStores::BoolAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStores* stores =
FindOrCreateServiceWorkerFetchStores(origin);
stores_task_runner_->PostTask(FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::Has,
base::Bind(&ServiceWorkerFetchStores::HasStore,
base::Unretained(stores),
key,
store_name,
callback));
}
void ServiceWorkerFetchStoresManager::Delete(
void ServiceWorkerFetchStoresManager::DeleteStore(
const GURL& origin,
const std::string& key,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) {
const std::string& store_name,
const ServiceWorkerFetchStores::BoolAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStores* stores =
FindOrCreateServiceWorkerFetchStores(origin);
stores_task_runner_->PostTask(FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::Delete,
base::Unretained(stores),
key,
callback));
stores_task_runner_->PostTask(
FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::DeleteStore,
base::Unretained(stores),
store_name,
callback));
}
void ServiceWorkerFetchStoresManager::Keys(
void ServiceWorkerFetchStoresManager::EnumerateStores(
const GURL& origin,
const ServiceWorkerFetchStores::StringsAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
......@@ -135,8 +136,9 @@ void ServiceWorkerFetchStoresManager::Keys(
stores_task_runner_->PostTask(
FROM_HERE,
base::Bind(
&ServiceWorkerFetchStores::Keys, base::Unretained(stores), callback));
base::Bind(&ServiceWorkerFetchStores::EnumerateStores,
base::Unretained(stores),
callback));
}
ServiceWorkerFetchStoresManager::ServiceWorkerFetchStoresManager(
......@@ -148,16 +150,15 @@ ServiceWorkerFetchStoresManager::ServiceWorkerFetchStoresManager(
ServiceWorkerFetchStores*
ServiceWorkerFetchStoresManager::FindOrCreateServiceWorkerFetchStores(
const GURL& origin) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStoresMap::const_iterator it =
service_worker_fetch_stores_.find(origin);
if (it == service_worker_fetch_stores_.end()) {
ServiceWorkerFetchStores::BackendType backend =
root_path_.empty()
? ServiceWorkerFetchStores::BACKEND_TYPE_MEMORY
: ServiceWorkerFetchStores::BACKEND_TYPE_SIMPLE_CACHE;
bool memory_only = root_path_.empty();
ServiceWorkerFetchStores* fetch_stores =
new ServiceWorkerFetchStores(ConstructOriginPath(root_path_, origin),
backend,
memory_only,
base::MessageLoopProxy::current());
// The map owns fetch_stores.
service_worker_fetch_stores_.insert(std::make_pair(origin, fetch_stores));
......
......@@ -22,6 +22,8 @@ namespace content {
// Keeps track of a ServiceWorkerFetchStores per origin. There is one
// ServiceWorkerFetchStoresManager per ServiceWorkerContextCore.
// TODO(jkarlin): Remove ServiceWorkerFetchStores from memory once they're no
// longer in active use.
class CONTENT_EXPORT ServiceWorkerFetchStoresManager {
public:
static scoped_ptr<ServiceWorkerFetchStoresManager> Create(
......@@ -37,19 +39,22 @@ class CONTENT_EXPORT ServiceWorkerFetchStoresManager {
// corresponding ServiceWorkerFetchStores method on the appropriate thread.
void CreateStore(
const GURL& origin,
const std::string& key,
const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback);
void Get(const GURL& origin,
const std::string& key,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback);
void Has(const GURL& origin,
const std::string& key,
const ServiceWorkerFetchStores::BoolAndErrorCallback& callback);
void Delete(const GURL& origin,
const std::string& key,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback);
void Keys(const GURL& origin,
const ServiceWorkerFetchStores::StringsAndErrorCallback& callback);
void GetStore(
const GURL& origin,
const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback);
void HasStore(const GURL& origin,
const std::string& store_name,
const ServiceWorkerFetchStores::BoolAndErrorCallback& callback);
void DeleteStore(
const GURL& origin,
const std::string& store_name,
const ServiceWorkerFetchStores::BoolAndErrorCallback& callback);
void EnumerateStores(
const GURL& origin,
const ServiceWorkerFetchStores::StringsAndErrorCallback& callback);
// TODO(jkarlin): Add match() function.
base::FilePath root_path() const { return root_path_; }
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/service_worker/service_worker_fetch_stores_manager.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
class ServiceWorkerFetchStoresManagerTest : public testing::Test {
protected:
ServiceWorkerFetchStoresManagerTest()
: browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
callback_bool_(false),
callback_store_(0),
callback_error_(ServiceWorkerFetchStores::FETCH_STORES_ERROR_NO_ERROR),
origin1_("http://example1.com"),
origin2_("http://example2.com") {}
virtual void SetUp() OVERRIDE {
if (MemoryOnly()) {
stores_ = ServiceWorkerFetchStoresManager::Create(
base::FilePath(), base::MessageLoopProxy::current());
} else {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
stores_ = ServiceWorkerFetchStoresManager::Create(
temp_dir_.path(), base::MessageLoopProxy::current());
}
}
virtual bool MemoryOnly() { return false; }
void BoolAndErrorCallback(base::RunLoop* run_loop,
bool value,
ServiceWorkerFetchStores::FetchStoresError error) {
callback_bool_ = value;
callback_error_ = error;
run_loop->Quit();
}
void StoreAndErrorCallback(base::RunLoop* run_loop,
int store,
ServiceWorkerFetchStores::FetchStoresError error) {
callback_store_ = store;
callback_error_ = error;
run_loop->Quit();
}
void StringsAndErrorCallback(
base::RunLoop* run_loop,
const std::vector<std::string>& strings,
ServiceWorkerFetchStores::FetchStoresError error) {
callback_strings_ = strings;
callback_error_ = error;
run_loop->Quit();
}
bool CreateStore(const GURL& origin, std::string key) {
scoped_ptr<base::RunLoop> loop(new base::RunLoop());
stores_->CreateStore(
origin,
key,
base::Bind(&ServiceWorkerFetchStoresManagerTest::StoreAndErrorCallback,
base::Unretained(this),
base::Unretained(loop.get())));
loop->Run();
bool error = callback_error_ !=
ServiceWorkerFetchStores::FETCH_STORES_ERROR_NO_ERROR;
if (error)
EXPECT_EQ(0, callback_store_);
else
EXPECT_LT(0, callback_store_);
return !error;
}
bool Get(const GURL& origin, std::string key) {
scoped_ptr<base::RunLoop> loop(new base::RunLoop());
stores_->GetStore(
origin,
key,
base::Bind(&ServiceWorkerFetchStoresManagerTest::StoreAndErrorCallback,
base::Unretained(this),
base::Unretained(loop.get())));
loop->Run();
bool error = callback_error_ !=
ServiceWorkerFetchStores::FETCH_STORES_ERROR_NO_ERROR;
if (error)
EXPECT_EQ(0, callback_store_);
else
EXPECT_LT(0, callback_store_);
return !error;
}
bool Has(const GURL& origin, std::string key) {
scoped_ptr<base::RunLoop> loop(new base::RunLoop());
stores_->HasStore(
origin,
key,
base::Bind(&ServiceWorkerFetchStoresManagerTest::BoolAndErrorCallback,
base::Unretained(this),
base::Unretained(loop.get())));
loop->Run();
return callback_bool_;
}
bool Delete(const GURL& origin, std::string key) {
scoped_ptr<base::RunLoop> loop(new base::RunLoop());
stores_->DeleteStore(
origin,
key,
base::Bind(&ServiceWorkerFetchStoresManagerTest::BoolAndErrorCallback,
base::Unretained(this),
base::Unretained(loop.get())));
loop->Run();
return callback_bool_;
}
bool Keys(const GURL& origin) {
scoped_ptr<base::RunLoop> loop(new base::RunLoop());
stores_->EnumerateStores(
origin,
base::Bind(
&ServiceWorkerFetchStoresManagerTest::StringsAndErrorCallback,
base::Unretained(this),
base::Unretained(loop.get())));
loop->Run();
bool error = callback_error_ !=
ServiceWorkerFetchStores::FETCH_STORES_ERROR_NO_ERROR;
return !error;
}
bool VerifyKeys(const std::vector<std::string>& expected_keys) {
if (expected_keys.size() != callback_strings_.size())
return false;
std::set<std::string> found_set;
for (int i = 0, max = callback_strings_.size(); i < max; ++i)
found_set.insert(callback_strings_[i]);
for (int i = 0, max = expected_keys.size(); i < max; ++i) {
if (found_set.find(expected_keys[i]) == found_set.end())
return false;
}
return true;
}
protected:
TestBrowserThreadBundle browser_thread_bundle_;
base::ScopedTempDir temp_dir_;
scoped_ptr<ServiceWorkerFetchStoresManager> stores_;
int callback_bool_;
int callback_store_;
ServiceWorkerFetchStores::FetchStoresError callback_error_;
std::vector<std::string> callback_strings_;
const GURL origin1_;
const GURL origin2_;
private:
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchStoresManagerTest);
};
class ServiceWorkerFetchStoresManagerMemoryOnlyTest
: public ServiceWorkerFetchStoresManagerTest {
virtual bool MemoryOnly() OVERRIDE { return true; }
};
class ServiceWorkerFetchStoresManagerTestP
: public ServiceWorkerFetchStoresManagerTest,
public testing::WithParamInterface<bool> {
virtual bool MemoryOnly() OVERRIDE { return !GetParam(); }
};
TEST_F(ServiceWorkerFetchStoresManagerTest, TestsRunOnIOThread) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, CreateStore) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, CreateDuplicateStore) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_FALSE(CreateStore(origin1_, "foo"));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_EXISTS,
callback_error_);
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, Create2Stores) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(CreateStore(origin1_, "bar"));
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, Create2StoresSameNameDiffSWs) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(CreateStore(origin2_, "foo"));
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, GetStore) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
int store = callback_store_;
EXPECT_TRUE(Get(origin1_, "foo"));
EXPECT_EQ(store, callback_store_);
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, GetNonExistent) {
EXPECT_FALSE(Get(origin1_, "foo"));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_NOT_FOUND,
callback_error_);
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, HasStore) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(Has(origin1_, "foo"));
EXPECT_TRUE(callback_bool_);
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, HasNonExistent) {
EXPECT_FALSE(Has(origin1_, "foo"));
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, DeleteStore) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(Delete(origin1_, "foo"));
EXPECT_FALSE(Get(origin1_, "foo"));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_NOT_FOUND,
callback_error_);
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, DeleteTwice) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(Delete(origin1_, "foo"));
EXPECT_FALSE(Delete(origin1_, "foo"));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_NOT_FOUND,
callback_error_);
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, EmptyKeys) {
EXPECT_TRUE(Keys(origin1_));
EXPECT_TRUE(callback_strings_.empty());
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, SomeKeys) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(CreateStore(origin1_, "bar"));
EXPECT_TRUE(CreateStore(origin2_, "baz"));
EXPECT_TRUE(Keys(origin1_));
EXPECT_EQ(2u, callback_strings_.size());
std::vector<std::string> expected_keys;
expected_keys.push_back("foo");
expected_keys.push_back("bar");
EXPECT_TRUE(VerifyKeys(expected_keys));
EXPECT_TRUE(Keys(origin2_));
EXPECT_EQ(1u, callback_strings_.size());
EXPECT_STREQ("baz", callback_strings_[0].c_str());
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, DeletedKeysGone) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(CreateStore(origin1_, "bar"));
EXPECT_TRUE(CreateStore(origin2_, "baz"));
EXPECT_TRUE(Delete(origin1_, "bar"));
EXPECT_TRUE(Keys(origin1_));
EXPECT_EQ(1u, callback_strings_.size());
EXPECT_STREQ("foo", callback_strings_[0].c_str());
}
TEST_P(ServiceWorkerFetchStoresManagerTestP, Chinese) {
EXPECT_TRUE(CreateStore(origin1_, "你好"));
int store = callback_store_;
EXPECT_TRUE(Get(origin1_, "你好"));
EXPECT_EQ(store, callback_store_);
EXPECT_TRUE(Keys(origin1_));
EXPECT_EQ(1u, callback_strings_.size());
EXPECT_TRUE("你好" == callback_strings_[0]);
}
TEST_F(ServiceWorkerFetchStoresManagerTest, EmptyKey) {
EXPECT_FALSE(CreateStore(origin1_, ""));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_EMPTY_KEY,
callback_error_);
EXPECT_FALSE(Get(origin1_, ""));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_EMPTY_KEY,
callback_error_);
EXPECT_FALSE(Has(origin1_, ""));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_EMPTY_KEY,
callback_error_);
EXPECT_FALSE(Delete(origin1_, ""));
EXPECT_EQ(ServiceWorkerFetchStores::FETCH_STORES_ERROR_EMPTY_KEY,
callback_error_);
}
TEST_F(ServiceWorkerFetchStoresManagerTest, DataPersists) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(CreateStore(origin1_, "bar"));
EXPECT_TRUE(CreateStore(origin1_, "baz"));
EXPECT_TRUE(CreateStore(origin2_, "raz"));
EXPECT_TRUE(Delete(origin1_, "bar"));
stores_ = ServiceWorkerFetchStoresManager::Create(stores_.get());
EXPECT_TRUE(Keys(origin1_));
EXPECT_EQ(2u, callback_strings_.size());
std::vector<std::string> expected_keys;
expected_keys.push_back("foo");
expected_keys.push_back("baz");
EXPECT_TRUE(VerifyKeys(expected_keys));
}
TEST_F(ServiceWorkerFetchStoresManagerMemoryOnlyTest, DataLostWhenMemoryOnly) {
EXPECT_TRUE(CreateStore(origin1_, "foo"));
EXPECT_TRUE(CreateStore(origin2_, "baz"));
stores_ = ServiceWorkerFetchStoresManager::Create(stores_.get());
EXPECT_TRUE(Keys(origin1_));
EXPECT_EQ(0u, callback_strings_.size());
}
TEST_F(ServiceWorkerFetchStoresManagerTest, BadStoreName) {
// Since the implementation writes store names to disk, ensure that we don't
// escape the directory.
const std::string bad_name = "../../../../../../../../../../../../../../foo";
EXPECT_TRUE(CreateStore(origin1_, bad_name));
EXPECT_TRUE(Keys(origin1_));
EXPECT_EQ(1u, callback_strings_.size());
EXPECT_STREQ(bad_name.c_str(), callback_strings_[0].c_str());
}
TEST_F(ServiceWorkerFetchStoresManagerTest, BadOriginName) {
// Since the implementation writes origin names to disk, ensure that we don't
// escape the directory.
GURL bad_origin("../../../../../../../../../../../../../../foo");
EXPECT_TRUE(CreateStore(bad_origin, "foo"));
EXPECT_TRUE(Keys(bad_origin));
EXPECT_EQ(1u, callback_strings_.size());
EXPECT_STREQ("foo", callback_strings_[0].c_str());
}
INSTANTIATE_TEST_CASE_P(ServiceWorkerFetchStoresManagerTests,
ServiceWorkerFetchStoresManagerTestP,
::testing::Values(false, true));
} // namespace content
{
'targets': [
{
# GN version: //content/browser/service_worker:database_proto
'target_name': 'database_proto',
# GN version: //content/browser/service_worker:proto
'target_name': 'proto',
'type': 'static_library',
'sources': [
'service_worker_cache.proto',
'service_worker_database.proto',
],
'variables': {
......
......@@ -23,7 +23,7 @@
'../ui/gfx/gfx.gyp:gfx_geometry',
'../ui/resources/ui_resources.gyp:ui_resources',
'../ui/snapshot/snapshot.gyp:snapshot',
'browser/service_worker/service_worker_proto.gyp:database_proto',
'browser/service_worker/service_worker_proto.gyp:proto',
'browser/speech/proto/speech_proto.gyp:speech_proto',
],
'export_dependent_settings': [
......@@ -1137,6 +1137,8 @@
'browser/service_worker/service_worker_dispatcher_host.h',
'browser/service_worker/service_worker_fetch_dispatcher.cc',
'browser/service_worker/service_worker_fetch_dispatcher.h',
'browser/service_worker/service_worker_fetch_store.cc',
'browser/service_worker/service_worker_fetch_store.h',
'browser/service_worker/service_worker_fetch_stores.cc',
'browser/service_worker/service_worker_fetch_stores.h',
'browser/service_worker/service_worker_fetch_stores_manager.cc',
......
......@@ -352,7 +352,7 @@
'target_name': 'content_unittests',
'type': '<(gtest_target_type)',
'dependencies': [
'browser/service_worker/service_worker_proto.gyp:database_proto',
'browser/service_worker/service_worker_proto.gyp:proto',
'browser/speech/proto/speech_proto.gyp:speech_proto',
'content.gyp:content_browser',
'content.gyp:content_common',
......@@ -595,7 +595,7 @@
'browser/service_worker/service_worker_controllee_request_handler_unittest.cc',
'browser/service_worker/service_worker_database_unittest.cc',
'browser/service_worker/service_worker_dispatcher_host_unittest.cc',
'browser/service_worker/service_worker_dispatcher_host_unittest.cc',
'browser/service_worker/service_worker_fetch_stores_manager_unittest.cc',
'browser/service_worker/service_worker_handle_unittest.cc',
'browser/service_worker/service_worker_job_unittest.cc',
'browser/service_worker/service_worker_provider_host_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment