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") { ...@@ -24,7 +24,7 @@ source_set("browser") {
deps = [ deps = [
"//base", "//base",
"//content:resources", "//content:resources",
"//content/browser/service_worker:database_proto", "//content/browser/service_worker:proto",
"//content/browser/speech/proto", "//content/browser/speech/proto",
"//crypto", "//crypto",
"//google_apis", "//google_apis",
......
...@@ -4,8 +4,9 @@ ...@@ -4,8 +4,9 @@
import("//third_party/protobuf/proto_library.gni") import("//third_party/protobuf/proto_library.gni")
proto_library("database_proto") { proto_library("proto") {
sources = [ sources = [
"service_worker_cache.proto",
"service_worker_database.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 @@ ...@@ -6,66 +6,329 @@
#include <string> #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 "content/public/browser/browser_thread.h"
#include "net/base/directory_lister.h"
namespace content { 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( ServiceWorkerFetchStores::ServiceWorkerFetchStores(
const base::FilePath& path, const base::FilePath& path,
BackendType backend_type, bool memory_only,
const scoped_refptr<base::MessageLoopProxy>& callback_loop) const scoped_refptr<base::MessageLoopProxy>& callback_loop)
: origin_path_(path), : initialized_(false), origin_path_(path), callback_loop_(callback_loop) {
backend_type_(backend_type), if (memory_only)
callback_loop_(callback_loop) { stores_loader_.reset(new MemoryLoader());
else
stores_loader_.reset(new SimpleCacheLoader(origin_path_));
} }
ServiceWorkerFetchStores::~ServiceWorkerFetchStores() { ServiceWorkerFetchStores::~ServiceWorkerFetchStores() {
} }
void ServiceWorkerFetchStores::CreateStore( void ServiceWorkerFetchStores::CreateStore(
const std::string& key, const std::string& store_name,
const StoreAndErrorCallback& callback) { 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, InitStore(store);
base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS));
return; stores_loader_->WriteIndex(&store_map_);
store->CreateBackend(
base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback,
base::Unretained(this),
store,
callback));
} }
void ServiceWorkerFetchStores::Get(const std::string& key, void ServiceWorkerFetchStores::GetStore(const std::string& store_name,
const StoreAndErrorCallback& callback) { 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;
}
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, store->CreateBackend(
base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback,
return; base::Unretained(this),
store,
callback));
} }
void ServiceWorkerFetchStores::Has(const std::string& key, void ServiceWorkerFetchStores::HasStore(const std::string& store_name,
const BoolAndErrorCallback& callback) const { const BoolAndErrorCallback& callback) {
// TODO(jkarlin): Implement this. 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( callback_loop_->PostTask(
FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EXISTS)); FROM_HERE,
return; base::Bind(
callback, has_store, FETCH_STORES_ERROR_NO_ERROR));
} }
void ServiceWorkerFetchStores::Delete(const std::string& key, void ServiceWorkerFetchStores::DeleteStore(
const StoreAndErrorCallback& callback) { const std::string& store_name,
// TODO(jkarlin): Implement this. 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, ServiceWorkerFetchStore* store = GetLoadedStore(store_name);
base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); if (!store) {
return; 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( void ServiceWorkerFetchStores::EnumerateStores(
const StringsAndErrorCallback& callback) const { const StringsAndErrorCallback& callback) {
// TODO(jkarlin): Implement this. LazyInit();
std::vector<std::string> out;
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( callback_loop_->PostTask(
FROM_HERE, base::Bind(callback, out, FETCH_STORES_ERROR_EXISTS)); FROM_HERE, base::Bind(callback, names, FETCH_STORES_ERROR_NO_ERROR));
return; }
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 } // namespace content
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_ #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_ #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_
#include <map>
#include <string> #include <string>
#include "base/callback.h" #include "base/callback.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/id_map.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
namespace base { namespace base {
...@@ -19,7 +21,8 @@ namespace content { ...@@ -19,7 +21,8 @@ namespace content {
class ServiceWorkerFetchStore; 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 // ServiceWorkerFetchStoresManager. Provided callbacks are called on the
// |callback_loop| provided in the constructor. // |callback_loop| provided in the constructor.
class ServiceWorkerFetchStores { class ServiceWorkerFetchStores {
...@@ -29,11 +32,10 @@ class ServiceWorkerFetchStores { ...@@ -29,11 +32,10 @@ class ServiceWorkerFetchStores {
FETCH_STORES_ERROR_NOT_IMPLEMENTED, FETCH_STORES_ERROR_NOT_IMPLEMENTED,
FETCH_STORES_ERROR_NOT_FOUND, FETCH_STORES_ERROR_NOT_FOUND,
FETCH_STORES_ERROR_EXISTS, 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(bool, FetchStoresError)> BoolAndErrorCallback;
typedef base::Callback<void(int, FetchStoresError)> StoreAndErrorCallback; typedef base::Callback<void(int, FetchStoresError)> StoreAndErrorCallback;
typedef base::Callback<void(const std::vector<std::string>&, typedef base::Callback<void(const std::vector<std::string>&,
...@@ -41,22 +43,60 @@ class ServiceWorkerFetchStores { ...@@ -41,22 +43,60 @@ class ServiceWorkerFetchStores {
ServiceWorkerFetchStores( ServiceWorkerFetchStores(
const base::FilePath& origin_path, const base::FilePath& origin_path,
BackendType backend, bool memory_only,
const scoped_refptr<base::MessageLoopProxy>& callback_loop); const scoped_refptr<base::MessageLoopProxy>& callback_loop);
virtual ~ServiceWorkerFetchStores(); 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); const StoreAndErrorCallback& callback);
void Get(const std::string& key, const StoreAndErrorCallback& callback);
void Has(const std::string& key, const BoolAndErrorCallback& callback) const; // Get the cache id for the given key. If not found returns
void Delete(const std::string& key, const StoreAndErrorCallback& callback); // FETCH_STORES_ERROR_NOT_FOUND.
void Keys(const StringsAndErrorCallback& callback) const; 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. // TODO(jkarlin): Add match() function.
void InitializeStoreCallback(const ServiceWorkerFetchStore* store,
const StoreAndErrorCallback& callback,
bool success);
private: 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_; base::FilePath origin_path_;
BackendType backend_type_;
scoped_refptr<base::MessageLoopProxy> callback_loop_; scoped_refptr<base::MessageLoopProxy> callback_loop_;
scoped_ptr<StoresLoader> stores_loader_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchStores); DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchStores);
}; };
......
...@@ -65,7 +65,7 @@ ServiceWorkerFetchStoresManager::~ServiceWorkerFetchStoresManager() { ...@@ -65,7 +65,7 @@ ServiceWorkerFetchStoresManager::~ServiceWorkerFetchStoresManager() {
void ServiceWorkerFetchStoresManager::CreateStore( void ServiceWorkerFetchStoresManager::CreateStore(
const GURL& origin, const GURL& origin,
const std::string& key, const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) { const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
...@@ -76,56 +76,57 @@ void ServiceWorkerFetchStoresManager::CreateStore( ...@@ -76,56 +76,57 @@ void ServiceWorkerFetchStoresManager::CreateStore(
FROM_HERE, FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::CreateStore, base::Bind(&ServiceWorkerFetchStores::CreateStore,
base::Unretained(stores), base::Unretained(stores),
key, store_name,
callback)); callback));
} }
void ServiceWorkerFetchStoresManager::Get( void ServiceWorkerFetchStoresManager::GetStore(
const GURL& origin, const GURL& origin,
const std::string& key, const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) { const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStores* stores = ServiceWorkerFetchStores* stores =
FindOrCreateServiceWorkerFetchStores(origin); FindOrCreateServiceWorkerFetchStores(origin);
stores_task_runner_->PostTask(FROM_HERE, stores_task_runner_->PostTask(FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::Get, base::Bind(&ServiceWorkerFetchStores::GetStore,
base::Unretained(stores), base::Unretained(stores),
key, store_name,
callback)); callback));
} }
void ServiceWorkerFetchStoresManager::Has( void ServiceWorkerFetchStoresManager::HasStore(
const GURL& origin, const GURL& origin,
const std::string& key, const std::string& store_name,
const ServiceWorkerFetchStores::BoolAndErrorCallback& callback) { const ServiceWorkerFetchStores::BoolAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStores* stores = ServiceWorkerFetchStores* stores =
FindOrCreateServiceWorkerFetchStores(origin); FindOrCreateServiceWorkerFetchStores(origin);
stores_task_runner_->PostTask(FROM_HERE, stores_task_runner_->PostTask(FROM_HERE,
base::Bind(&ServiceWorkerFetchStores::Has, base::Bind(&ServiceWorkerFetchStores::HasStore,
base::Unretained(stores), base::Unretained(stores),
key, store_name,
callback)); callback));
} }
void ServiceWorkerFetchStoresManager::Delete( void ServiceWorkerFetchStoresManager::DeleteStore(
const GURL& origin, const GURL& origin,
const std::string& key, const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) { const ServiceWorkerFetchStores::BoolAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStores* stores = ServiceWorkerFetchStores* stores =
FindOrCreateServiceWorkerFetchStores(origin); FindOrCreateServiceWorkerFetchStores(origin);
stores_task_runner_->PostTask(FROM_HERE, stores_task_runner_->PostTask(
base::Bind(&ServiceWorkerFetchStores::Delete, FROM_HERE,
base::Unretained(stores), base::Bind(&ServiceWorkerFetchStores::DeleteStore,
key, base::Unretained(stores),
callback)); store_name,
callback));
} }
void ServiceWorkerFetchStoresManager::Keys( void ServiceWorkerFetchStoresManager::EnumerateStores(
const GURL& origin, const GURL& origin,
const ServiceWorkerFetchStores::StringsAndErrorCallback& callback) { const ServiceWorkerFetchStores::StringsAndErrorCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
...@@ -135,8 +136,9 @@ void ServiceWorkerFetchStoresManager::Keys( ...@@ -135,8 +136,9 @@ void ServiceWorkerFetchStoresManager::Keys(
stores_task_runner_->PostTask( stores_task_runner_->PostTask(
FROM_HERE, FROM_HERE,
base::Bind( base::Bind(&ServiceWorkerFetchStores::EnumerateStores,
&ServiceWorkerFetchStores::Keys, base::Unretained(stores), callback)); base::Unretained(stores),
callback));
} }
ServiceWorkerFetchStoresManager::ServiceWorkerFetchStoresManager( ServiceWorkerFetchStoresManager::ServiceWorkerFetchStoresManager(
...@@ -148,16 +150,15 @@ ServiceWorkerFetchStoresManager::ServiceWorkerFetchStoresManager( ...@@ -148,16 +150,15 @@ ServiceWorkerFetchStoresManager::ServiceWorkerFetchStoresManager(
ServiceWorkerFetchStores* ServiceWorkerFetchStores*
ServiceWorkerFetchStoresManager::FindOrCreateServiceWorkerFetchStores( ServiceWorkerFetchStoresManager::FindOrCreateServiceWorkerFetchStores(
const GURL& origin) { const GURL& origin) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerFetchStoresMap::const_iterator it = ServiceWorkerFetchStoresMap::const_iterator it =
service_worker_fetch_stores_.find(origin); service_worker_fetch_stores_.find(origin);
if (it == service_worker_fetch_stores_.end()) { if (it == service_worker_fetch_stores_.end()) {
ServiceWorkerFetchStores::BackendType backend = bool memory_only = root_path_.empty();
root_path_.empty()
? ServiceWorkerFetchStores::BACKEND_TYPE_MEMORY
: ServiceWorkerFetchStores::BACKEND_TYPE_SIMPLE_CACHE;
ServiceWorkerFetchStores* fetch_stores = ServiceWorkerFetchStores* fetch_stores =
new ServiceWorkerFetchStores(ConstructOriginPath(root_path_, origin), new ServiceWorkerFetchStores(ConstructOriginPath(root_path_, origin),
backend, memory_only,
base::MessageLoopProxy::current()); base::MessageLoopProxy::current());
// The map owns fetch_stores. // The map owns fetch_stores.
service_worker_fetch_stores_.insert(std::make_pair(origin, fetch_stores)); service_worker_fetch_stores_.insert(std::make_pair(origin, fetch_stores));
......
...@@ -22,6 +22,8 @@ namespace content { ...@@ -22,6 +22,8 @@ namespace content {
// Keeps track of a ServiceWorkerFetchStores per origin. There is one // Keeps track of a ServiceWorkerFetchStores per origin. There is one
// ServiceWorkerFetchStoresManager per ServiceWorkerContextCore. // ServiceWorkerFetchStoresManager per ServiceWorkerContextCore.
// TODO(jkarlin): Remove ServiceWorkerFetchStores from memory once they're no
// longer in active use.
class CONTENT_EXPORT ServiceWorkerFetchStoresManager { class CONTENT_EXPORT ServiceWorkerFetchStoresManager {
public: public:
static scoped_ptr<ServiceWorkerFetchStoresManager> Create( static scoped_ptr<ServiceWorkerFetchStoresManager> Create(
...@@ -37,19 +39,22 @@ class CONTENT_EXPORT ServiceWorkerFetchStoresManager { ...@@ -37,19 +39,22 @@ class CONTENT_EXPORT ServiceWorkerFetchStoresManager {
// corresponding ServiceWorkerFetchStores method on the appropriate thread. // corresponding ServiceWorkerFetchStores method on the appropriate thread.
void CreateStore( void CreateStore(
const GURL& origin, const GURL& origin,
const std::string& key, const std::string& store_name,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback); const ServiceWorkerFetchStores::StoreAndErrorCallback& callback);
void Get(const GURL& origin, void GetStore(
const std::string& key, const GURL& origin,
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback); const std::string& store_name,
void Has(const GURL& origin, const ServiceWorkerFetchStores::StoreAndErrorCallback& callback);
const std::string& key, void HasStore(const GURL& origin,
const ServiceWorkerFetchStores::BoolAndErrorCallback& callback); const std::string& store_name,
void Delete(const GURL& origin, const ServiceWorkerFetchStores::BoolAndErrorCallback& callback);
const std::string& key, void DeleteStore(
const ServiceWorkerFetchStores::StoreAndErrorCallback& callback); const GURL& origin,
void Keys(const GURL& origin, const std::string& store_name,
const ServiceWorkerFetchStores::StringsAndErrorCallback& callback); const ServiceWorkerFetchStores::BoolAndErrorCallback& callback);
void EnumerateStores(
const GURL& origin,
const ServiceWorkerFetchStores::StringsAndErrorCallback& callback);
// TODO(jkarlin): Add match() function. // TODO(jkarlin): Add match() function.
base::FilePath root_path() const { return root_path_; } 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': [ 'targets': [
{ {
# GN version: //content/browser/service_worker:database_proto # GN version: //content/browser/service_worker:proto
'target_name': 'database_proto', 'target_name': 'proto',
'type': 'static_library', 'type': 'static_library',
'sources': [ 'sources': [
'service_worker_cache.proto',
'service_worker_database.proto', 'service_worker_database.proto',
], ],
'variables': { 'variables': {
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
'../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gfx/gfx.gyp:gfx_geometry',
'../ui/resources/ui_resources.gyp:ui_resources', '../ui/resources/ui_resources.gyp:ui_resources',
'../ui/snapshot/snapshot.gyp:snapshot', '../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', 'browser/speech/proto/speech_proto.gyp:speech_proto',
], ],
'export_dependent_settings': [ 'export_dependent_settings': [
...@@ -1137,6 +1137,8 @@ ...@@ -1137,6 +1137,8 @@
'browser/service_worker/service_worker_dispatcher_host.h', 'browser/service_worker/service_worker_dispatcher_host.h',
'browser/service_worker/service_worker_fetch_dispatcher.cc', 'browser/service_worker/service_worker_fetch_dispatcher.cc',
'browser/service_worker/service_worker_fetch_dispatcher.h', '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.cc',
'browser/service_worker/service_worker_fetch_stores.h', 'browser/service_worker/service_worker_fetch_stores.h',
'browser/service_worker/service_worker_fetch_stores_manager.cc', 'browser/service_worker/service_worker_fetch_stores_manager.cc',
......
...@@ -352,7 +352,7 @@ ...@@ -352,7 +352,7 @@
'target_name': 'content_unittests', 'target_name': 'content_unittests',
'type': '<(gtest_target_type)', 'type': '<(gtest_target_type)',
'dependencies': [ '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', 'browser/speech/proto/speech_proto.gyp:speech_proto',
'content.gyp:content_browser', 'content.gyp:content_browser',
'content.gyp:content_common', 'content.gyp:content_common',
...@@ -595,7 +595,7 @@ ...@@ -595,7 +595,7 @@
'browser/service_worker/service_worker_controllee_request_handler_unittest.cc', 'browser/service_worker/service_worker_controllee_request_handler_unittest.cc',
'browser/service_worker/service_worker_database_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_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_handle_unittest.cc',
'browser/service_worker/service_worker_job_unittest.cc', 'browser/service_worker/service_worker_job_unittest.cc',
'browser/service_worker/service_worker_provider_host_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