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_database.h" | 5 #include "content/browser/service_worker/service_worker_database.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/location.h" | 10 #include "base/location.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
13 #include "base/strings/string_split.h" | |
14 #include "base/strings/string_util.h" | |
15 #include "base/strings/stringprintf.h" | |
16 #include "content/browser/service_worker/service_worker_database.pb.h" | |
13 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" | 17 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" |
14 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 18 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
15 #include "third_party/leveldatabase/src/include/leveldb/env.h" | 19 #include "third_party/leveldatabase/src/include/leveldb/env.h" |
16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
17 | 21 |
18 // LevelDB database schema | 22 // LevelDB database schema |
19 // ======================= | 23 // ======================= |
20 // | 24 // |
21 // NOTE | 25 // NOTE |
22 // - int64 value is serialized as a string by base::Int64ToString(). | 26 // - int64 value is serialized as a string by base::Int64ToString(). |
23 // | 27 // |
24 // Version 1 (in sorted order) | 28 // Version 1 (in sorted order) |
25 // key: "DB_VERSION" | 29 // key: "INITDATA_DB_VERSION" |
26 // value: "1" | 30 // value: "1" |
27 // | 31 // |
28 // key: "NEXT_REGISTRATION_ID" | 32 // key: "INITDATA_NEXT_REGISTRATION_ID" |
29 // value: <int64 'next_available_registration_id'> | 33 // value: <int64 'next_available_registration_id'> |
30 // | 34 // |
31 // key: "NEXT_RESOURCE_ID" | 35 // key: "INITDATA_NEXT_RESOURCE_ID" |
32 // value: <int64 'next_available_resource_id'> | 36 // value: <int64 'next_available_resource_id'> |
33 // | 37 // |
34 // key: "NEXT_VERSION_ID" | 38 // key: "INITDATA_NEXT_VERSION_ID" |
35 // value: <int64 'next_available_version_id'> | 39 // value: <int64 'next_available_version_id'> |
40 // | |
41 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin' serialized by GURL::spec()> | |
42 // value: <empty> | |
43 // | |
44 // key: "REG:" + (1) + '\x00' + (2) | |
45 // (1) <GURL 'origin' serialized by GURL::spec()> | |
46 // (2) <int64 'registration_id'> | |
47 // (ex. "REG:http://example.com\x00123456") | |
48 // value: <ServiceWorkerRegistrationData serialized as a string> | |
36 | 49 |
37 namespace content { | 50 namespace content { |
38 | 51 |
39 namespace { | 52 namespace { |
40 | 53 |
41 const char kDatabaseVersionKey[] = "DB_VERSION"; | 54 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION"; |
42 const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID"; | 55 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID"; |
43 const char kNextResIdKey[] = "NEXT_RESOURCE_ID"; | 56 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID"; |
44 const char kNextVerIdKey[] = "NEXT_VERSION_ID"; | 57 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID"; |
58 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:"; | |
59 | |
60 const char kRegKeyPrefix[] = "REG:"; | |
61 const char kKeySeparator = '\x00'; | |
45 | 62 |
46 const int64 kCurrentSchemaVersion = 1; | 63 const int64 kCurrentSchemaVersion = 1; |
47 | 64 |
65 bool RemovePrefix(const std::string& str, | |
66 const std::string& prefix, | |
67 std::string* out) { | |
68 if (!StartsWithASCII(str, prefix, true)) | |
69 return false; | |
70 if (out) | |
71 *out = str.substr(prefix.size()); | |
72 return true; | |
73 } | |
74 | |
75 std::string CreateRegistrationKey(int64 registration_id, | |
76 const GURL& origin) { | |
77 return base::StringPrintf("%s%s%c%s", | |
78 kRegKeyPrefix, | |
79 origin.spec().c_str(), | |
80 kKeySeparator, | |
81 base::Int64ToString(registration_id).c_str()); | |
82 } | |
83 | |
84 std::string CreateUniqueOriginKey(const GURL& origin) { | |
85 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str()); | |
86 } | |
87 | |
88 void PutRegistrationDataToBatch( | |
89 const ServiceWorkerDatabase::RegistrationData& input, | |
90 leveldb::WriteBatch* batch) { | |
91 DCHECK(batch); | |
92 | |
93 // Convert RegistrationData to ServiceWorkerRegistrationData. | |
94 ServiceWorkerRegistrationData data; | |
95 data.set_registration_id(input.registration_id); | |
96 data.set_scope_url(input.scope.spec()); | |
97 data.set_script_url(input.script.spec()); | |
98 data.set_version_id(input.version_id); | |
99 data.set_is_active(input.is_active); | |
100 data.set_has_fetch_handler(input.has_fetch_handler); | |
101 data.set_last_update_check_time(input.last_update_check.ToInternalValue()); | |
102 | |
103 std::string value; | |
104 bool success = data.SerializeToString(&value); | |
105 DCHECK(success); | |
106 GURL origin = GURL(data.scope_url()).GetOrigin(); | |
michaeln
2014/04/26 00:29:02
could use input.scope.GetOrigin()
nhiroki
2014/04/28 04:15:59
Done.
| |
107 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value); | |
108 } | |
109 | |
110 void PutUniqueOriginToBatch(const GURL& origin, | |
111 leveldb::WriteBatch* batch) { | |
112 // Value should be empty. | |
113 batch->Put(CreateUniqueOriginKey(origin), ""); | |
114 } | |
115 | |
116 bool ParseRegistrationData( | |
117 const std::string& serialized, | |
118 ServiceWorkerDatabase::RegistrationData* out) { | |
119 DCHECK(out); | |
120 ServiceWorkerRegistrationData data; | |
121 if (!data.ParseFromString(serialized)) | |
122 return false; | |
123 | |
124 GURL scope_url(data.scope_url()); | |
125 GURL script_url(data.script_url()); | |
126 if (scope_url.GetOrigin() != script_url.GetOrigin()) | |
127 return false; | |
128 | |
129 // Convert ServiceWorkerRegistrationData to RegistrationData. | |
130 out->registration_id = data.registration_id(); | |
131 out->scope = scope_url; | |
132 out->script = script_url; | |
133 out->version_id = data.version_id(); | |
134 out->is_active = data.is_active(); | |
135 out->has_fetch_handler = data.has_fetch_handler(); | |
136 out->last_update_check = | |
137 base::Time::FromInternalValue(data.last_update_check_time()); | |
138 return true; | |
139 } | |
140 | |
48 } // namespace | 141 } // namespace |
49 | 142 |
50 ServiceWorkerDatabase::RegistrationData::RegistrationData() | 143 ServiceWorkerDatabase::RegistrationData::RegistrationData() |
51 : registration_id(-1), | 144 : registration_id(-1), |
52 version_id(-1), | 145 version_id(-1), |
53 is_active(false), | 146 is_active(false), |
54 has_fetch_handler(false) { | 147 has_fetch_handler(false) { |
55 } | 148 } |
56 | 149 |
57 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { | 150 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { |
58 } | 151 } |
59 | 152 |
60 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path) | 153 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path) |
61 : path_(path), | 154 : path_(path), |
155 next_avail_registration_id_(0), | |
156 next_avail_resource_id_(0), | |
157 next_avail_version_id_(0), | |
62 is_disabled_(false), | 158 is_disabled_(false), |
63 was_corruption_detected_(false) { | 159 was_corruption_detected_(false) { |
64 } | 160 } |
65 | 161 |
66 ServiceWorkerDatabase::~ServiceWorkerDatabase() { | 162 ServiceWorkerDatabase::~ServiceWorkerDatabase() { |
67 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 163 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
68 db_.reset(); | 164 db_.reset(); |
69 } | 165 } |
70 | 166 |
71 bool ServiceWorkerDatabase::GetNextAvailableIds( | 167 bool ServiceWorkerDatabase::GetNextAvailableIds( |
72 int64* next_avail_registration_id, | 168 int64* next_avail_registration_id, |
73 int64* next_avail_version_id, | 169 int64* next_avail_version_id, |
74 int64* next_avail_resource_id) { | 170 int64* next_avail_resource_id) { |
75 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 171 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
76 DCHECK(next_avail_registration_id); | 172 DCHECK(next_avail_registration_id); |
77 DCHECK(next_avail_version_id); | 173 DCHECK(next_avail_version_id); |
78 DCHECK(next_avail_resource_id); | 174 DCHECK(next_avail_resource_id); |
79 | 175 |
80 if (!LazyOpen(false) || is_disabled_) | 176 if (!LazyOpen(false) || is_disabled_) |
81 return false; | 177 return false; |
82 | 178 |
83 int64 reg_id = -1; | 179 if (!ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_) || |
84 int64 ver_id = -1; | 180 !ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_) || |
85 int64 res_id = -1; | 181 !ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_)) { |
182 return false; | |
183 } | |
86 | 184 |
87 if (!ReadNextAvailableId(kNextRegIdKey, ®_id) || | 185 *next_avail_registration_id = next_avail_registration_id_; |
88 !ReadNextAvailableId(kNextVerIdKey, &ver_id) || | 186 *next_avail_version_id = next_avail_version_id_; |
89 !ReadNextAvailableId(kNextResIdKey, &res_id)) | 187 *next_avail_resource_id = next_avail_resource_id_; |
188 return true; | |
189 } | |
190 | |
191 bool ServiceWorkerDatabase::GetOriginsWithRegistrations( | |
192 std::set<GURL>* origins) { | |
193 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
194 DCHECK(origins); | |
195 | |
196 if (!LazyOpen(false) || is_disabled_) | |
90 return false; | 197 return false; |
91 | 198 |
92 *next_avail_registration_id = reg_id; | 199 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
93 *next_avail_version_id = ver_id; | 200 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { |
94 *next_avail_resource_id = res_id; | 201 if (!itr->status().ok()) { |
202 HandleError(FROM_HERE, itr->status()); | |
203 origins->clear(); | |
204 return false; | |
205 } | |
206 | |
207 std::string origin; | |
208 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin)) | |
209 break; | |
210 origins->insert(GURL(origin)); | |
211 } | |
95 return true; | 212 return true; |
96 } | 213 } |
97 | 214 |
215 bool ServiceWorkerDatabase::GetRegistrationsForOrigin( | |
216 const GURL& origin, | |
217 std::vector<RegistrationData>* registrations) { | |
218 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
219 DCHECK(registrations); | |
220 | |
221 if (!LazyOpen(false) || is_disabled_) | |
222 return false; | |
223 | |
224 // Create a key prefix for registrations. | |
225 std::string prefix = base::StringPrintf( | |
226 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator); | |
227 | |
228 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | |
229 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | |
230 if (!itr->status().ok()) { | |
231 HandleError(FROM_HERE, itr->status()); | |
232 registrations->clear(); | |
233 return false; | |
234 } | |
235 | |
236 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) | |
237 break; | |
238 | |
239 RegistrationData registration; | |
240 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { | |
241 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
242 registrations->clear(); | |
243 return false; | |
244 } | |
245 registrations->push_back(registration); | |
246 } | |
247 return true; | |
248 } | |
249 | |
250 bool ServiceWorkerDatabase::ReadRegistration( | |
251 int64 registration_id, | |
252 const GURL& origin, | |
253 RegistrationData* registration, | |
254 std::vector<ResourceRecord>* resources) { | |
255 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
256 DCHECK(registration); | |
257 DCHECK(resources); | |
258 | |
259 if (!LazyOpen(false) || is_disabled_) | |
260 return false; | |
261 | |
262 RegistrationData value; | |
263 if (!ReadRegistrationData(registration_id, origin, &value)) | |
264 return false; | |
265 | |
266 // TODO(nhiroki): Read ResourceRecords tied with this registration. | |
267 | |
268 *registration = value; | |
269 return true; | |
270 } | |
271 | |
272 bool ServiceWorkerDatabase::WriteRegistration( | |
273 const RegistrationData& registration, | |
274 const std::vector<ResourceRecord>& resources) { | |
275 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
276 if (!LazyOpen(true) || is_disabled_) | |
277 return false; | |
278 | |
279 leveldb::WriteBatch batch; | |
280 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch); | |
281 BumpNextVersionIdIfNeeded(registration.version_id, &batch); | |
282 | |
283 // TODO(nhiroki): Skip to add the origin into the unique origin list if it | |
284 // has already been added. | |
285 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch); | |
286 | |
287 PutRegistrationDataToBatch(registration, &batch); | |
288 | |
289 // TODO(nhiroki): Write |resources| into the database. | |
290 return WriteBatch(&batch); | |
291 } | |
292 | |
293 bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id, | |
294 const GURL& origin) { | |
295 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
296 if (!LazyOpen(false) || is_disabled_) | |
297 return false; | |
298 | |
299 RegistrationData registration; | |
300 if (!ReadRegistrationData(registration_id, origin, ®istration)) | |
301 return false; | |
302 | |
303 registration.is_active = true; | |
304 | |
305 leveldb::WriteBatch batch; | |
306 PutRegistrationDataToBatch(registration, &batch); | |
307 return WriteBatch(&batch); | |
308 } | |
309 | |
310 bool ServiceWorkerDatabase::UpdateLastCheckTime(int64 registration_id, | |
311 const GURL& origin, | |
312 const base::Time& time) { | |
313 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
314 if (!LazyOpen(false) || is_disabled_) | |
315 return false; | |
316 | |
317 RegistrationData registration; | |
318 if (!ReadRegistrationData(registration_id, origin, ®istration)) | |
319 return false; | |
320 | |
321 registration.last_update_check = time; | |
322 | |
323 leveldb::WriteBatch batch; | |
324 PutRegistrationDataToBatch(registration, &batch); | |
325 return WriteBatch(&batch); | |
326 } | |
327 | |
328 bool ServiceWorkerDatabase::DeleteRegistration(int64 registration_id, | |
329 const GURL& origin) { | |
330 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | |
331 if (!LazyOpen(false) || is_disabled_) | |
332 return false; | |
333 | |
334 leveldb::WriteBatch batch; | |
335 | |
336 // Remove |origin| from unique origins if a registration specified by | |
337 // |registration_id| is the only one for |origin|. | |
338 // TODO(nhiroki): Check the uniqueness by more efficient way. | |
339 std::vector<RegistrationData> registrations; | |
340 if (!GetRegistrationsForOrigin(origin, ®istrations)) | |
341 return false; | |
342 if (registrations.size() == 1 && | |
343 registrations[0].registration_id == registration_id) { | |
344 batch.Delete(CreateUniqueOriginKey(origin)); | |
345 } | |
346 | |
347 batch.Delete(CreateRegistrationKey(registration_id, origin)); | |
348 | |
349 // TODO(nhiroki): Delete ResourceRecords tied with this registration. | |
350 return WriteBatch(&batch); | |
351 } | |
352 | |
98 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { | 353 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { |
99 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 354 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
100 if (IsOpen()) | 355 if (IsOpen()) |
101 return true; | 356 return true; |
102 | 357 |
103 // Do not try to open a database if we tried and failed once. | 358 // Do not try to open a database if we tried and failed once. |
104 if (is_disabled_) | 359 if (is_disabled_) |
105 return false; | 360 return false; |
106 | 361 |
107 // When |path_| is empty, open a database in-memory. | 362 // When |path_| is empty, open a database in-memory. |
(...skipping 19 matching lines...) Expand all Loading... | |
127 leveldb::Status status = | 382 leveldb::Status status = |
128 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); | 383 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); |
129 if (!status.ok()) { | 384 if (!status.ok()) { |
130 DCHECK(!db); | 385 DCHECK(!db); |
131 // TODO(nhiroki): Should we retry to open the database? | 386 // TODO(nhiroki): Should we retry to open the database? |
132 HandleError(FROM_HERE, status); | 387 HandleError(FROM_HERE, status); |
133 return false; | 388 return false; |
134 } | 389 } |
135 db_.reset(db); | 390 db_.reset(db); |
136 | 391 |
137 if (IsEmpty() && !PopulateInitialData()) { | 392 int64 db_version; |
nhiroki
2014/04/25 11:05:01
fyi: Removed all write operations from LazyOpen().
michaeln
2014/04/26 00:29:02
fantastique ;)
| |
138 DLOG(ERROR) << "Failed to populate the database."; | 393 if (!ReadDatabaseVersion(&db_version)) |
139 db_.reset(); | |
140 return false; | 394 return false; |
141 } | 395 if (db_version > 0) |
396 is_initialized_ = true; | |
142 return true; | 397 return true; |
143 } | 398 } |
144 | 399 |
145 bool ServiceWorkerDatabase::PopulateInitialData() { | |
146 leveldb::WriteBatch batch; | |
147 batch.Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); | |
148 batch.Put(kNextRegIdKey, base::Int64ToString(0)); | |
149 batch.Put(kNextResIdKey, base::Int64ToString(0)); | |
150 batch.Put(kNextVerIdKey, base::Int64ToString(0)); | |
151 return WriteBatch(&batch); | |
152 } | |
153 | |
154 bool ServiceWorkerDatabase::ReadNextAvailableId( | 400 bool ServiceWorkerDatabase::ReadNextAvailableId( |
155 const char* id_key, int64* next_avail_id) { | 401 const char* id_key, int64* next_avail_id) { |
156 DCHECK(id_key); | 402 DCHECK(id_key); |
157 DCHECK(next_avail_id); | 403 DCHECK(next_avail_id); |
158 | 404 |
159 std::string value; | 405 std::string value; |
160 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); | 406 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); |
407 if (status.IsNotFound()) { | |
408 // Nobody has gotten the next resource id for |id_key|. | |
409 *next_avail_id = 0; | |
410 return true; | |
411 } | |
412 | |
161 if (!status.ok()) { | 413 if (!status.ok()) { |
162 HandleError(FROM_HERE, status); | 414 HandleError(FROM_HERE, status); |
163 return false; | 415 return false; |
164 } | 416 } |
165 | 417 |
166 int64 parsed; | 418 int64 parsed; |
167 if (!base::StringToInt64(value, &parsed)) { | 419 if (!base::StringToInt64(value, &parsed)) { |
168 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 420 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
169 return false; | 421 return false; |
170 } | 422 } |
171 | 423 |
172 *next_avail_id = parsed; | 424 *next_avail_id = parsed; |
173 return true; | 425 return true; |
174 } | 426 } |
175 | 427 |
428 bool ServiceWorkerDatabase::ReadRegistrationData( | |
429 int64 registration_id, | |
430 const GURL& origin, | |
431 RegistrationData* registration) { | |
432 DCHECK(registration); | |
433 | |
434 std::string key = CreateRegistrationKey(registration_id, origin); | |
435 | |
436 std::string value; | |
437 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); | |
438 if (!status.ok()) { | |
439 if (!status.IsNotFound()) | |
440 HandleError(FROM_HERE, status); | |
441 return false; | |
442 } | |
443 | |
444 RegistrationData parsed; | |
445 if (!ParseRegistrationData(value, &parsed)) { | |
446 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
447 return false; | |
448 } | |
449 | |
450 *registration = parsed; | |
451 return true; | |
452 } | |
453 | |
454 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64* db_version) { | |
455 std::string value; | |
456 leveldb::Status status = | |
457 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); | |
458 if (status.IsNotFound()) { | |
459 // The database hasn't been initialized yet. | |
460 *db_version = 0; | |
461 return true; | |
462 } | |
463 if (!status.ok()) { | |
464 HandleError(FROM_HERE, status); | |
465 return false; | |
466 } | |
467 | |
468 int64 parsed; | |
469 if (!base::StringToInt64(value, &parsed)) { | |
470 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
471 return false; | |
472 } | |
473 | |
michaeln
2014/04/26 00:29:02
??
const int kFirstValidVersion = 1;
if (parsed <
nhiroki
2014/04/28 04:15:59
Done.
| |
474 *db_version = parsed; | |
475 return true; | |
476 } | |
477 | |
176 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) { | 478 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) { |
177 DCHECK(batch); | 479 DCHECK(batch); |
178 DCHECK(!is_disabled_); | 480 DCHECK(!is_disabled_); |
481 | |
482 if (!is_initialized_) { | |
483 // Write the database schema version. | |
484 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); | |
485 is_initialized_ = true; | |
486 } | |
487 | |
179 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); | 488 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); |
180 if (!status.ok()) { | 489 if (!status.ok()) { |
181 HandleError(FROM_HERE, status); | 490 HandleError(FROM_HERE, status); |
182 return false; | 491 return false; |
183 } | 492 } |
184 return true; | 493 return true; |
185 } | 494 } |
186 | 495 |
496 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( | |
497 int64 used_id, leveldb::WriteBatch* batch) { | |
498 DCHECK(batch); | |
499 if (next_avail_registration_id_ <= used_id) { | |
500 next_avail_registration_id_ = used_id + 1; | |
501 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_)); | |
502 } | |
503 } | |
504 | |
505 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded( | |
506 int64 used_id, leveldb::WriteBatch* batch) { | |
507 DCHECK(batch); | |
508 if (next_avail_version_id_ <= used_id) { | |
509 next_avail_version_id_ = used_id + 1; | |
510 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_)); | |
511 } | |
512 } | |
513 | |
187 bool ServiceWorkerDatabase::IsOpen() { | 514 bool ServiceWorkerDatabase::IsOpen() { |
188 return db_.get() != NULL; | 515 return db_.get() != NULL; |
189 } | 516 } |
190 | 517 |
191 bool ServiceWorkerDatabase::IsEmpty() { | |
192 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | |
193 itr->SeekToFirst(); | |
194 // TODO(nhiroki): Handle an error. | |
195 DCHECK(itr->status().ok()); | |
196 return !itr->Valid(); | |
197 } | |
198 | |
199 void ServiceWorkerDatabase::HandleError( | 518 void ServiceWorkerDatabase::HandleError( |
200 const tracked_objects::Location& from_here, | 519 const tracked_objects::Location& from_here, |
201 const leveldb::Status& status) { | 520 const leveldb::Status& status) { |
202 // TODO(nhiroki): Add an UMA histogram. | 521 // TODO(nhiroki): Add an UMA histogram. |
203 DLOG(ERROR) << "Failed at: " << from_here.ToString() | 522 DLOG(ERROR) << "Failed at: " << from_here.ToString() |
204 << " with error: " << status.ToString(); | 523 << " with error: " << status.ToString(); |
205 is_disabled_ = true; | 524 is_disabled_ = true; |
206 if (status.IsCorruption()) | 525 if (status.IsCorruption()) |
207 was_corruption_detected_ = true; | 526 was_corruption_detected_ = true; |
208 db_.reset(); | 527 db_.reset(); |
209 } | 528 } |
210 | 529 |
211 } // namespace content | 530 } // namespace content |
OLD | NEW |