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