OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/service_worker/service_worker_fetch_stores.h" | 5 #include "content/browser/service_worker/service_worker_fetch_stores.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
| 9 #include "base/file_util.h" |
| 10 #include "base/files/memory_mapped_file.h" |
| 11 #include "base/sha1.h" |
| 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "base/strings/string_util.h" |
| 14 #include "content/browser/service_worker/service_worker_cache.pb.h" |
| 15 #include "content/browser/service_worker/service_worker_fetch_store.h" |
9 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 17 #include "net/base/directory_lister.h" |
10 | 18 |
11 namespace content { | 19 namespace content { |
12 | 20 |
| 21 // Handles the loading of ServiceWorkerFetchStore and any extra clean up other |
| 22 // than deleting the ServiceWorkerFetchStore object. |
| 23 class ServiceWorkerFetchStores::StoresLoader { |
| 24 public: |
| 25 virtual ~StoresLoader() {}; |
| 26 virtual ServiceWorkerFetchStore* LoadStore(const std::string& key) = 0; |
| 27 // Creates a new store, deleting any pre-existing store of the same name. |
| 28 virtual ServiceWorkerFetchStore* CreateStore(const std::string& key) = 0; |
| 29 virtual bool CleanUpDeletedStore(const std::string& key) = 0; |
| 30 virtual bool WriteIndex(StoreMap* stores) = 0; |
| 31 virtual void LoadIndex(std::vector<std::string>* names) = 0; |
| 32 }; |
| 33 |
| 34 class ServiceWorkerFetchStores::MemoryLoader |
| 35 : public ServiceWorkerFetchStores::StoresLoader { |
| 36 public: |
| 37 virtual content::ServiceWorkerFetchStore* LoadStore( |
| 38 const std::string& key) OVERRIDE { |
| 39 NOTREACHED(); |
| 40 return NULL; |
| 41 } |
| 42 |
| 43 virtual ServiceWorkerFetchStore* CreateStore( |
| 44 const std::string& key) OVERRIDE { |
| 45 return ServiceWorkerFetchStore::CreateMemoryStore(key); |
| 46 } |
| 47 |
| 48 virtual bool CleanUpDeletedStore(const std::string& key) OVERRIDE { |
| 49 return true; |
| 50 } |
| 51 |
| 52 virtual bool WriteIndex(StoreMap* stores) OVERRIDE { return false; } |
| 53 |
| 54 virtual void LoadIndex(std::vector<std::string>* names) OVERRIDE { return; } |
| 55 }; |
| 56 |
| 57 class ServiceWorkerFetchStores::SimpleCacheLoader |
| 58 : public ServiceWorkerFetchStores::StoresLoader { |
| 59 public: |
| 60 explicit SimpleCacheLoader(const base::FilePath& origin_path) |
| 61 : origin_path_(origin_path) {} |
| 62 |
| 63 virtual ServiceWorkerFetchStore* LoadStore(const std::string& key) OVERRIDE { |
| 64 base::CreateDirectory(CreatePersistentStorePath(origin_path_, key)); |
| 65 return ServiceWorkerFetchStore::CreatePersistentStore( |
| 66 CreatePersistentStorePath(origin_path_, key), key); |
| 67 } |
| 68 |
| 69 virtual ServiceWorkerFetchStore* CreateStore( |
| 70 const std::string& key) OVERRIDE { |
| 71 base::FilePath store_path = CreatePersistentStorePath(origin_path_, key); |
| 72 if (base::PathExists(store_path)) |
| 73 base::DeleteFile(store_path, /* recursive */ true); |
| 74 return LoadStore(key); |
| 75 } |
| 76 |
| 77 virtual bool CleanUpDeletedStore(const std::string& key) OVERRIDE { |
| 78 return base::DeleteFile(CreatePersistentStorePath(origin_path_, key), true); |
| 79 } |
| 80 |
| 81 virtual bool WriteIndex(StoreMap* stores) OVERRIDE { |
| 82 ServiceWorkerCacheStorageIndex index; |
| 83 |
| 84 for (StoreMap::const_iterator iter(stores); !iter.IsAtEnd(); |
| 85 iter.Advance()) { |
| 86 const ServiceWorkerFetchStore* store = iter.GetCurrentValue(); |
| 87 ServiceWorkerCacheStorageIndex::Cache* cache = index.add_cache(); |
| 88 cache->set_name(store->name()); |
| 89 cache->set_size(0); // TODO(jkarlin): Make this real. |
| 90 } |
| 91 |
| 92 std::string serialized; |
| 93 bool success = index.SerializeToString(&serialized); |
| 94 DCHECK(success); |
| 95 |
| 96 base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp"); |
| 97 base::FilePath index_path = origin_path_.AppendASCII("index.txt"); |
| 98 |
| 99 int bytes_written = |
| 100 base::WriteFile(tmp_path, serialized.c_str(), serialized.size()); |
| 101 if (bytes_written != implicit_cast<int>(serialized.size())) { |
| 102 base::DeleteFile(tmp_path, /* recursive */ false); |
| 103 return false; |
| 104 } |
| 105 |
| 106 // Atomically rename the temporary index file to become the real one. |
| 107 return base::ReplaceFile(tmp_path, index_path, NULL); |
| 108 } |
| 109 |
| 110 virtual void LoadIndex(std::vector<std::string>* names) OVERRIDE { |
| 111 base::FilePath index_path = origin_path_.AppendASCII("index.txt"); |
| 112 |
| 113 std::string serialized; |
| 114 if (!base::ReadFileToString(index_path, &serialized)) |
| 115 return; |
| 116 |
| 117 ServiceWorkerCacheStorageIndex index; |
| 118 index.ParseFromString(serialized); |
| 119 |
| 120 for (int i = 0, max = index.cache_size(); i < max; ++i) { |
| 121 const ServiceWorkerCacheStorageIndex::Cache& cache = index.cache(i); |
| 122 names->push_back(cache.name()); |
| 123 } |
| 124 |
| 125 // TODO(jkarlin): Delete stores that are in the directory and not returned |
| 126 // in LoadIndex. |
| 127 return; |
| 128 } |
| 129 |
| 130 private: |
| 131 std::string HexedHash(const std::string& value) { |
| 132 std::string value_hash = base::SHA1HashString(value); |
| 133 std::string valued_hexed_hash = base::StringToLowerASCII( |
| 134 base::HexEncode(value_hash.c_str(), value_hash.length())); |
| 135 return valued_hexed_hash; |
| 136 } |
| 137 |
| 138 base::FilePath CreatePersistentStorePath(const base::FilePath& origin_path, |
| 139 const std::string& store_name) { |
| 140 return origin_path.AppendASCII(HexedHash(store_name)); |
| 141 } |
| 142 |
| 143 const base::FilePath origin_path_; |
| 144 }; |
| 145 |
13 ServiceWorkerFetchStores::ServiceWorkerFetchStores( | 146 ServiceWorkerFetchStores::ServiceWorkerFetchStores( |
14 const base::FilePath& path, | 147 const base::FilePath& path, |
15 BackendType backend_type, | 148 bool memory_only, |
16 const scoped_refptr<base::MessageLoopProxy>& callback_loop) | 149 const scoped_refptr<base::MessageLoopProxy>& callback_loop) |
17 : origin_path_(path), | 150 : initialized_(false), origin_path_(path), callback_loop_(callback_loop) { |
18 backend_type_(backend_type), | 151 if (memory_only) |
19 callback_loop_(callback_loop) { | 152 stores_loader_.reset(new MemoryLoader()); |
| 153 else |
| 154 stores_loader_.reset(new SimpleCacheLoader(origin_path_)); |
20 } | 155 } |
21 | 156 |
22 ServiceWorkerFetchStores::~ServiceWorkerFetchStores() { | 157 ServiceWorkerFetchStores::~ServiceWorkerFetchStores() { |
23 } | 158 } |
24 | 159 |
25 void ServiceWorkerFetchStores::CreateStore( | 160 void ServiceWorkerFetchStores::CreateStore( |
26 const std::string& key, | 161 const std::string& store_name, |
27 const StoreAndErrorCallback& callback) { | 162 const StoreAndErrorCallback& callback) { |
28 // TODO(jkarlin): Implement this. | 163 LazyInit(); |
29 | 164 |
30 callback_loop_->PostTask(FROM_HERE, | 165 if (store_name.empty()) { |
31 base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); | 166 callback_loop_->PostTask( |
32 return; | 167 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY)); |
33 } | 168 return; |
34 | 169 } |
35 void ServiceWorkerFetchStores::Get(const std::string& key, | 170 |
36 const StoreAndErrorCallback& callback) { | 171 if (GetLoadedStore(store_name)) { |
37 // TODO(jkarlin): Implement this. | 172 callback_loop_->PostTask( |
38 | 173 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); |
39 callback_loop_->PostTask(FROM_HERE, | 174 return; |
40 base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); | 175 } |
41 return; | 176 |
42 } | 177 ServiceWorkerFetchStore* store = stores_loader_->CreateStore(store_name); |
43 | 178 |
44 void ServiceWorkerFetchStores::Has(const std::string& key, | 179 if (!store) { |
45 const BoolAndErrorCallback& callback) const { | 180 callback_loop_->PostTask( |
46 // TODO(jkarlin): Implement this. | 181 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_STORAGE)); |
| 182 return; |
| 183 } |
| 184 |
| 185 InitStore(store); |
| 186 |
| 187 stores_loader_->WriteIndex(&store_map_); |
| 188 |
| 189 store->CreateBackend( |
| 190 base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback, |
| 191 base::Unretained(this), |
| 192 store, |
| 193 callback)); |
| 194 } |
| 195 |
| 196 void ServiceWorkerFetchStores::GetStore(const std::string& store_name, |
| 197 const StoreAndErrorCallback& callback) { |
| 198 LazyInit(); |
| 199 |
| 200 if (store_name.empty()) { |
| 201 callback_loop_->PostTask( |
| 202 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY)); |
| 203 return; |
| 204 } |
| 205 |
| 206 ServiceWorkerFetchStore* store = GetLoadedStore(store_name); |
| 207 if (!store) { |
| 208 callback_loop_->PostTask( |
| 209 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_NOT_FOUND)); |
| 210 return; |
| 211 } |
| 212 |
| 213 store->CreateBackend( |
| 214 base::Bind(&ServiceWorkerFetchStores::InitializeStoreCallback, |
| 215 base::Unretained(this), |
| 216 store, |
| 217 callback)); |
| 218 } |
| 219 |
| 220 void ServiceWorkerFetchStores::HasStore(const std::string& store_name, |
| 221 const BoolAndErrorCallback& callback) { |
| 222 LazyInit(); |
| 223 |
| 224 if (store_name.empty()) { |
| 225 callback_loop_->PostTask( |
| 226 FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EMPTY_KEY)); |
| 227 return; |
| 228 } |
| 229 |
| 230 bool has_store = GetLoadedStore(store_name) != NULL; |
47 | 231 |
48 callback_loop_->PostTask( | 232 callback_loop_->PostTask( |
49 FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EXISTS)); | 233 FROM_HERE, |
50 return; | 234 base::Bind( |
51 } | 235 callback, has_store, FETCH_STORES_ERROR_NO_ERROR)); |
52 | 236 } |
53 void ServiceWorkerFetchStores::Delete(const std::string& key, | 237 |
54 const StoreAndErrorCallback& callback) { | 238 void ServiceWorkerFetchStores::DeleteStore( |
55 // TODO(jkarlin): Implement this. | 239 const std::string& store_name, |
56 | 240 const BoolAndErrorCallback& callback) { |
57 callback_loop_->PostTask(FROM_HERE, | 241 LazyInit(); |
58 base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); | 242 |
59 return; | 243 if (store_name.empty()) { |
60 } | 244 callback_loop_->PostTask( |
61 | 245 FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EMPTY_KEY)); |
62 void ServiceWorkerFetchStores::Keys( | 246 return; |
63 const StringsAndErrorCallback& callback) const { | 247 } |
64 // TODO(jkarlin): Implement this. | 248 |
65 std::vector<std::string> out; | 249 ServiceWorkerFetchStore* store = GetLoadedStore(store_name); |
| 250 if (!store) { |
| 251 callback_loop_->PostTask( |
| 252 FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_NOT_FOUND)); |
| 253 return; |
| 254 } |
| 255 |
| 256 name_map_.erase(store_name); |
| 257 store_map_.Remove(store->id()); // deletes store |
| 258 |
| 259 stores_loader_->WriteIndex(&store_map_); // Update the index. |
| 260 |
| 261 stores_loader_->CleanUpDeletedStore(store_name); |
| 262 |
66 callback_loop_->PostTask( | 263 callback_loop_->PostTask( |
67 FROM_HERE, base::Bind(callback, out, FETCH_STORES_ERROR_EXISTS)); | 264 FROM_HERE, base::Bind(callback, true, FETCH_STORES_ERROR_NO_ERROR)); |
68 return; | 265 } |
| 266 |
| 267 void ServiceWorkerFetchStores::EnumerateStores( |
| 268 const StringsAndErrorCallback& callback) { |
| 269 LazyInit(); |
| 270 |
| 271 std::vector<std::string> names; |
| 272 for (NameMap::const_iterator it = name_map_.begin(); it != name_map_.end(); |
| 273 ++it) { |
| 274 names.push_back(it->first); |
| 275 } |
| 276 |
| 277 callback_loop_->PostTask( |
| 278 FROM_HERE, base::Bind(callback, names, FETCH_STORES_ERROR_NO_ERROR)); |
| 279 } |
| 280 |
| 281 void ServiceWorkerFetchStores::InitializeStoreCallback( |
| 282 const ServiceWorkerFetchStore* store, |
| 283 const StoreAndErrorCallback& callback, |
| 284 bool success) { |
| 285 if (!success) { |
| 286 // TODO(jkarlin): This should delete the directory and try again in case |
| 287 // the cache is simply corrupt. |
| 288 callback_loop_->PostTask( |
| 289 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_STORAGE)); |
| 290 return; |
| 291 } |
| 292 callback_loop_->PostTask( |
| 293 FROM_HERE, |
| 294 base::Bind(callback, store->id(), FETCH_STORES_ERROR_NO_ERROR)); |
| 295 } |
| 296 |
| 297 // Init is run lazily so that it is called on the proper MessageLoop. |
| 298 void ServiceWorkerFetchStores::LazyInit() { |
| 299 if (initialized_) |
| 300 return; |
| 301 |
| 302 std::vector<std::string> indexed_store_names; |
| 303 stores_loader_->LoadIndex(&indexed_store_names); |
| 304 |
| 305 for (std::vector<std::string>::const_iterator it = |
| 306 indexed_store_names.begin(); |
| 307 it != indexed_store_names.end(); |
| 308 ++it) { |
| 309 ServiceWorkerFetchStore* store = stores_loader_->LoadStore(*it); |
| 310 InitStore(store); |
| 311 } |
| 312 initialized_ = true; |
| 313 } |
| 314 |
| 315 void ServiceWorkerFetchStores::InitStore(ServiceWorkerFetchStore* store) { |
| 316 StoreID id = store_map_.Add(store); |
| 317 name_map_.insert(std::make_pair(store->name(), id)); |
| 318 store->set_id(id); |
| 319 } |
| 320 |
| 321 ServiceWorkerFetchStore* ServiceWorkerFetchStores::GetLoadedStore( |
| 322 const std::string& key) const { |
| 323 DCHECK(initialized_); |
| 324 |
| 325 NameMap::const_iterator it = name_map_.find(key); |
| 326 if (it == name_map_.end()) |
| 327 return NULL; |
| 328 |
| 329 ServiceWorkerFetchStore* store = store_map_.Lookup(it->second); |
| 330 DCHECK(store); |
| 331 return store; |
69 } | 332 } |
70 | 333 |
71 } // namespace content | 334 } // namespace content |
OLD | NEW |