OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/service_worker/service_worker_fetch_stores.h" | |
6 | |
7 #include <string> | |
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" | |
16 #include "content/public/browser/browser_thread.h" | |
17 #include "net/base/directory_lister.h" | |
18 | |
19 namespace content { | |
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 | |
146 ServiceWorkerFetchStores::ServiceWorkerFetchStores( | |
147 const base::FilePath& path, | |
148 bool memory_only, | |
149 const scoped_refptr<base::MessageLoopProxy>& callback_loop) | |
150 : initialized_(false), origin_path_(path), callback_loop_(callback_loop) { | |
151 if (memory_only) | |
152 stores_loader_.reset(new MemoryLoader()); | |
153 else | |
154 stores_loader_.reset(new SimpleCacheLoader(origin_path_)); | |
155 } | |
156 | |
157 ServiceWorkerFetchStores::~ServiceWorkerFetchStores() { | |
158 } | |
159 | |
160 void ServiceWorkerFetchStores::CreateStore( | |
161 const std::string& store_name, | |
162 const StoreAndErrorCallback& callback) { | |
163 LazyInit(); | |
164 | |
165 if (store_name.empty()) { | |
166 callback_loop_->PostTask( | |
167 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EMPTY_KEY)); | |
168 return; | |
169 } | |
170 | |
171 if (GetLoadedStore(store_name)) { | |
172 callback_loop_->PostTask( | |
173 FROM_HERE, base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); | |
174 return; | |
175 } | |
176 | |
177 ServiceWorkerFetchStore* store = stores_loader_->CreateStore(store_name); | |
178 | |
179 if (!store) { | |
180 callback_loop_->PostTask( | |
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; | |
231 | |
232 callback_loop_->PostTask( | |
233 FROM_HERE, | |
234 base::Bind( | |
235 callback, has_store, FETCH_STORES_ERROR_NO_ERROR)); | |
236 } | |
237 | |
238 void ServiceWorkerFetchStores::DeleteStore( | |
239 const std::string& store_name, | |
240 const BoolAndErrorCallback& callback) { | |
241 LazyInit(); | |
242 | |
243 if (store_name.empty()) { | |
244 callback_loop_->PostTask( | |
245 FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EMPTY_KEY)); | |
246 return; | |
247 } | |
248 | |
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 | |
263 callback_loop_->PostTask( | |
264 FROM_HERE, base::Bind(callback, true, FETCH_STORES_ERROR_NO_ERROR)); | |
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; | |
332 } | |
333 | |
334 } // namespace content | |
OLD | NEW |