OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "webkit/quota/quota_database.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/auto_reset.h" | |
10 #include "base/bind.h" | |
11 #include "base/file_util.h" | |
12 #include "base/time.h" | |
13 #include "googleurl/src/gurl.h" | |
14 #include "sql/connection.h" | |
15 #include "sql/meta_table.h" | |
16 #include "sql/statement.h" | |
17 #include "sql/transaction.h" | |
18 #include "webkit/quota/special_storage_policy.h" | |
19 | |
20 namespace quota { | |
21 namespace { | |
22 | |
23 // Definitions for database schema. | |
24 | |
25 const int kCurrentVersion = 4; | |
26 const int kCompatibleVersion = 2; | |
27 | |
28 const char kHostQuotaTable[] = "HostQuotaTable"; | |
29 const char kOriginInfoTable[] = "OriginInfoTable"; | |
30 const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped"; | |
31 | |
32 bool VerifyValidQuotaConfig(const char* key) { | |
33 return (key != NULL && | |
34 (!strcmp(key, QuotaDatabase::kDesiredAvailableSpaceKey) || | |
35 !strcmp(key, QuotaDatabase::kTemporaryQuotaOverrideKey))); | |
36 } | |
37 | |
38 const int kCommitIntervalMs = 30000; | |
39 | |
40 } // anonymous namespace | |
41 | |
42 // static | |
43 const char QuotaDatabase::kDesiredAvailableSpaceKey[] = "DesiredAvailableSpace"; | |
44 const char QuotaDatabase::kTemporaryQuotaOverrideKey[] = | |
45 "TemporaryQuotaOverride"; | |
46 | |
47 const QuotaDatabase::TableSchema QuotaDatabase::kTables[] = { | |
48 { kHostQuotaTable, | |
49 "(host TEXT NOT NULL," | |
50 " type INTEGER NOT NULL," | |
51 " quota INTEGER DEFAULT 0," | |
52 " UNIQUE(host, type))" }, | |
53 { kOriginInfoTable, | |
54 "(origin TEXT NOT NULL," | |
55 " type INTEGER NOT NULL," | |
56 " used_count INTEGER DEFAULT 0," | |
57 " last_access_time INTEGER DEFAULT 0," | |
58 " last_modified_time INTEGER DEFAULT 0," | |
59 " UNIQUE(origin, type))" }, | |
60 }; | |
61 | |
62 // static | |
63 const QuotaDatabase::IndexSchema QuotaDatabase::kIndexes[] = { | |
64 { "HostIndex", | |
65 kHostQuotaTable, | |
66 "(host)", | |
67 false }, | |
68 { "OriginInfoIndex", | |
69 kOriginInfoTable, | |
70 "(origin)", | |
71 false }, | |
72 { "OriginLastAccessTimeIndex", | |
73 kOriginInfoTable, | |
74 "(last_access_time)", | |
75 false }, | |
76 { "OriginLastModifiedTimeIndex", | |
77 kOriginInfoTable, | |
78 "(last_modified_time)", | |
79 false }, | |
80 }; | |
81 | |
82 struct QuotaDatabase::QuotaTableImporter { | |
83 bool Append(const QuotaTableEntry& entry) { | |
84 entries.push_back(entry); | |
85 return true; | |
86 } | |
87 std::vector<QuotaTableEntry> entries; | |
88 }; | |
89 | |
90 // Clang requires explicit out-of-line constructors for them. | |
91 QuotaDatabase::QuotaTableEntry::QuotaTableEntry() | |
92 : type(kStorageTypeUnknown), | |
93 quota(0) { | |
94 } | |
95 | |
96 QuotaDatabase::QuotaTableEntry::QuotaTableEntry( | |
97 const std::string& host, | |
98 StorageType type, | |
99 int64 quota) | |
100 : host(host), | |
101 type(type), | |
102 quota(quota) { | |
103 } | |
104 | |
105 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry() | |
106 : type(kStorageTypeUnknown), | |
107 used_count(0) { | |
108 } | |
109 | |
110 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry( | |
111 const GURL& origin, | |
112 StorageType type, | |
113 int used_count, | |
114 const base::Time& last_access_time, | |
115 const base::Time& last_modified_time) | |
116 : origin(origin), | |
117 type(type), | |
118 used_count(used_count), | |
119 last_access_time(last_access_time), | |
120 last_modified_time(last_modified_time) { | |
121 } | |
122 | |
123 // QuotaDatabase ------------------------------------------------------------ | |
124 QuotaDatabase::QuotaDatabase(const base::FilePath& path) | |
125 : db_file_path_(path), | |
126 is_recreating_(false), | |
127 is_disabled_(false) { | |
128 } | |
129 | |
130 QuotaDatabase::~QuotaDatabase() { | |
131 if (db_) { | |
132 db_->CommitTransaction(); | |
133 } | |
134 } | |
135 | |
136 void QuotaDatabase::CloseConnection() { | |
137 meta_table_.reset(); | |
138 db_.reset(); | |
139 } | |
140 | |
141 bool QuotaDatabase::GetHostQuota( | |
142 const std::string& host, StorageType type, int64* quota) { | |
143 DCHECK(quota); | |
144 if (!LazyOpen(false)) | |
145 return false; | |
146 | |
147 const char* kSql = | |
148 "SELECT quota" | |
149 " FROM HostQuotaTable" | |
150 " WHERE host = ? AND type = ?"; | |
151 | |
152 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
153 statement.BindString(0, host); | |
154 statement.BindInt(1, static_cast<int>(type)); | |
155 | |
156 if (!statement.Step()) | |
157 return false; | |
158 | |
159 *quota = statement.ColumnInt64(0); | |
160 return true; | |
161 } | |
162 | |
163 bool QuotaDatabase::SetHostQuota( | |
164 const std::string& host, StorageType type, int64 quota) { | |
165 DCHECK_GE(quota, 0); | |
166 if (!LazyOpen(true)) | |
167 return false; | |
168 | |
169 const char* kSql = | |
170 "INSERT OR REPLACE INTO HostQuotaTable" | |
171 " (quota, host, type)" | |
172 " VALUES (?, ?, ?)"; | |
173 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
174 statement.BindInt64(0, quota); | |
175 statement.BindString(1, host); | |
176 statement.BindInt(2, static_cast<int>(type)); | |
177 | |
178 if (!statement.Run()) | |
179 return false; | |
180 | |
181 ScheduleCommit(); | |
182 return true; | |
183 } | |
184 | |
185 bool QuotaDatabase::SetOriginLastAccessTime( | |
186 const GURL& origin, StorageType type, base::Time last_access_time) { | |
187 if (!LazyOpen(true)) | |
188 return false; | |
189 | |
190 sql::Statement statement; | |
191 | |
192 int used_count = 1; | |
193 if (FindOriginUsedCount(origin, type, &used_count)) { | |
194 ++used_count; | |
195 const char* kSql = | |
196 "UPDATE OriginInfoTable" | |
197 " SET used_count = ?, last_access_time = ?" | |
198 " WHERE origin = ? AND type = ?"; | |
199 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
200 } else { | |
201 const char* kSql = | |
202 "INSERT INTO OriginInfoTable" | |
203 " (used_count, last_access_time, origin, type)" | |
204 " VALUES (?, ?, ?, ?)"; | |
205 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
206 } | |
207 statement.BindInt(0, used_count); | |
208 statement.BindInt64(1, last_access_time.ToInternalValue()); | |
209 statement.BindString(2, origin.spec()); | |
210 statement.BindInt(3, static_cast<int>(type)); | |
211 | |
212 if (!statement.Run()) | |
213 return false; | |
214 | |
215 ScheduleCommit(); | |
216 return true; | |
217 } | |
218 | |
219 bool QuotaDatabase::SetOriginLastModifiedTime( | |
220 const GURL& origin, StorageType type, base::Time last_modified_time) { | |
221 if (!LazyOpen(true)) | |
222 return false; | |
223 | |
224 sql::Statement statement; | |
225 | |
226 int dummy; | |
227 if (FindOriginUsedCount(origin, type, &dummy)) { | |
228 const char* kSql = | |
229 "UPDATE OriginInfoTable" | |
230 " SET last_modified_time = ?" | |
231 " WHERE origin = ? AND type = ?"; | |
232 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
233 } else { | |
234 const char* kSql = | |
235 "INSERT INTO OriginInfoTable" | |
236 " (last_modified_time, origin, type) VALUES (?, ?, ?)"; | |
237 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
238 } | |
239 statement.BindInt64(0, last_modified_time.ToInternalValue()); | |
240 statement.BindString(1, origin.spec()); | |
241 statement.BindInt(2, static_cast<int>(type)); | |
242 | |
243 if (!statement.Run()) | |
244 return false; | |
245 | |
246 ScheduleCommit(); | |
247 return true; | |
248 } | |
249 | |
250 bool QuotaDatabase::RegisterInitialOriginInfo( | |
251 const std::set<GURL>& origins, StorageType type) { | |
252 if (!LazyOpen(true)) | |
253 return false; | |
254 | |
255 typedef std::set<GURL>::const_iterator itr_type; | |
256 for (itr_type itr = origins.begin(), end = origins.end(); | |
257 itr != end; ++itr) { | |
258 const char* kSql = | |
259 "INSERT OR IGNORE INTO OriginInfoTable" | |
260 " (origin, type) VALUES (?, ?)"; | |
261 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
262 statement.BindString(0, itr->spec()); | |
263 statement.BindInt(1, static_cast<int>(type)); | |
264 | |
265 if (!statement.Run()) | |
266 return false; | |
267 } | |
268 | |
269 ScheduleCommit(); | |
270 return true; | |
271 } | |
272 | |
273 bool QuotaDatabase::DeleteHostQuota( | |
274 const std::string& host, StorageType type) { | |
275 if (!LazyOpen(false)) | |
276 return false; | |
277 | |
278 const char* kSql = | |
279 "DELETE FROM HostQuotaTable" | |
280 " WHERE host = ? AND type = ?"; | |
281 | |
282 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
283 statement.BindString(0, host); | |
284 statement.BindInt(1, static_cast<int>(type)); | |
285 | |
286 if (!statement.Run()) | |
287 return false; | |
288 | |
289 ScheduleCommit(); | |
290 return true; | |
291 } | |
292 | |
293 bool QuotaDatabase::DeleteOriginInfo( | |
294 const GURL& origin, StorageType type) { | |
295 if (!LazyOpen(false)) | |
296 return false; | |
297 | |
298 const char* kSql = | |
299 "DELETE FROM OriginInfoTable" | |
300 " WHERE origin = ? AND type = ?"; | |
301 | |
302 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
303 statement.BindString(0, origin.spec()); | |
304 statement.BindInt(1, static_cast<int>(type)); | |
305 | |
306 if (!statement.Run()) | |
307 return false; | |
308 | |
309 ScheduleCommit(); | |
310 return true; | |
311 } | |
312 | |
313 bool QuotaDatabase::GetQuotaConfigValue(const char* key, int64* value) { | |
314 if (!LazyOpen(false)) | |
315 return false; | |
316 DCHECK(VerifyValidQuotaConfig(key)); | |
317 return meta_table_->GetValue(key, value); | |
318 } | |
319 | |
320 bool QuotaDatabase::SetQuotaConfigValue(const char* key, int64 value) { | |
321 if (!LazyOpen(true)) | |
322 return false; | |
323 DCHECK(VerifyValidQuotaConfig(key)); | |
324 return meta_table_->SetValue(key, value); | |
325 } | |
326 | |
327 bool QuotaDatabase::GetLRUOrigin( | |
328 StorageType type, | |
329 const std::set<GURL>& exceptions, | |
330 SpecialStoragePolicy* special_storage_policy, | |
331 GURL* origin) { | |
332 DCHECK(origin); | |
333 if (!LazyOpen(false)) | |
334 return false; | |
335 | |
336 const char* kSql = "SELECT origin FROM OriginInfoTable" | |
337 " WHERE type = ?" | |
338 " ORDER BY last_access_time ASC"; | |
339 | |
340 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
341 statement.BindInt(0, static_cast<int>(type)); | |
342 | |
343 while (statement.Step()) { | |
344 GURL url(statement.ColumnString(0)); | |
345 if (exceptions.find(url) != exceptions.end()) | |
346 continue; | |
347 if (special_storage_policy && | |
348 special_storage_policy->IsStorageUnlimited(url)) | |
349 continue; | |
350 *origin = url; | |
351 return true; | |
352 } | |
353 | |
354 *origin = GURL(); | |
355 return statement.Succeeded(); | |
356 } | |
357 | |
358 bool QuotaDatabase::GetOriginsModifiedSince( | |
359 StorageType type, std::set<GURL>* origins, base::Time modified_since) { | |
360 DCHECK(origins); | |
361 if (!LazyOpen(false)) | |
362 return false; | |
363 | |
364 const char* kSql = "SELECT origin FROM OriginInfoTable" | |
365 " WHERE type = ? AND last_modified_time >= ?"; | |
366 | |
367 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
368 statement.BindInt(0, static_cast<int>(type)); | |
369 statement.BindInt64(1, modified_since.ToInternalValue()); | |
370 | |
371 origins->clear(); | |
372 while (statement.Step()) | |
373 origins->insert(GURL(statement.ColumnString(0))); | |
374 | |
375 return statement.Succeeded(); | |
376 } | |
377 | |
378 bool QuotaDatabase::IsOriginDatabaseBootstrapped() { | |
379 if (!LazyOpen(true)) | |
380 return false; | |
381 | |
382 int flag = 0; | |
383 return meta_table_->GetValue(kIsOriginTableBootstrapped, &flag) && flag; | |
384 } | |
385 | |
386 bool QuotaDatabase::SetOriginDatabaseBootstrapped(bool bootstrap_flag) { | |
387 if (!LazyOpen(true)) | |
388 return false; | |
389 | |
390 return meta_table_->SetValue(kIsOriginTableBootstrapped, bootstrap_flag); | |
391 } | |
392 | |
393 void QuotaDatabase::Commit() { | |
394 if (!db_) | |
395 return; | |
396 | |
397 if (timer_.IsRunning()) | |
398 timer_.Stop(); | |
399 | |
400 db_->CommitTransaction(); | |
401 db_->BeginTransaction(); | |
402 } | |
403 | |
404 void QuotaDatabase::ScheduleCommit() { | |
405 if (timer_.IsRunning()) | |
406 return; | |
407 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCommitIntervalMs), | |
408 this, &QuotaDatabase::Commit); | |
409 } | |
410 | |
411 bool QuotaDatabase::FindOriginUsedCount( | |
412 const GURL& origin, StorageType type, int* used_count) { | |
413 DCHECK(used_count); | |
414 if (!LazyOpen(false)) | |
415 return false; | |
416 | |
417 const char* kSql = | |
418 "SELECT used_count FROM OriginInfoTable" | |
419 " WHERE origin = ? AND type = ?"; | |
420 | |
421 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
422 statement.BindString(0, origin.spec()); | |
423 statement.BindInt(1, static_cast<int>(type)); | |
424 | |
425 if (!statement.Step()) | |
426 return false; | |
427 | |
428 *used_count = statement.ColumnInt(0); | |
429 return true; | |
430 } | |
431 | |
432 bool QuotaDatabase::LazyOpen(bool create_if_needed) { | |
433 if (db_) | |
434 return true; | |
435 | |
436 // If we tried and failed once, don't try again in the same session | |
437 // to avoid creating an incoherent mess on disk. | |
438 if (is_disabled_) | |
439 return false; | |
440 | |
441 bool in_memory_only = db_file_path_.empty(); | |
442 if (!create_if_needed && | |
443 (in_memory_only || !file_util::PathExists(db_file_path_))) { | |
444 return false; | |
445 } | |
446 | |
447 db_.reset(new sql::Connection); | |
448 meta_table_.reset(new sql::MetaTable); | |
449 | |
450 db_->set_histogram_tag("Quota"); | |
451 | |
452 bool opened = false; | |
453 if (in_memory_only) { | |
454 opened = db_->OpenInMemory(); | |
455 } else if (!file_util::CreateDirectory(db_file_path_.DirName())) { | |
456 LOG(ERROR) << "Failed to create quota database directory."; | |
457 } else { | |
458 opened = db_->Open(db_file_path_); | |
459 if (opened) | |
460 db_->Preload(); | |
461 } | |
462 | |
463 if (!opened || !EnsureDatabaseVersion()) { | |
464 LOG(ERROR) << "Failed to open the quota database."; | |
465 is_disabled_ = true; | |
466 db_.reset(); | |
467 meta_table_.reset(); | |
468 return false; | |
469 } | |
470 | |
471 // Start a long-running transaction. | |
472 db_->BeginTransaction(); | |
473 | |
474 return true; | |
475 } | |
476 | |
477 bool QuotaDatabase::EnsureDatabaseVersion() { | |
478 static const size_t kTableCount = ARRAYSIZE_UNSAFE(kTables); | |
479 static const size_t kIndexCount = ARRAYSIZE_UNSAFE(kIndexes); | |
480 if (!sql::MetaTable::DoesTableExist(db_.get())) | |
481 return CreateSchema(db_.get(), meta_table_.get(), | |
482 kCurrentVersion, kCompatibleVersion, | |
483 kTables, kTableCount, | |
484 kIndexes, kIndexCount); | |
485 | |
486 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion)) | |
487 return false; | |
488 | |
489 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) { | |
490 LOG(WARNING) << "Quota database is too new."; | |
491 return false; | |
492 } | |
493 | |
494 if (meta_table_->GetVersionNumber() < kCurrentVersion) { | |
495 if (!UpgradeSchema(meta_table_->GetVersionNumber())) | |
496 return ResetSchema(); | |
497 } | |
498 | |
499 #ifndef NDEBUG | |
500 DCHECK(sql::MetaTable::DoesTableExist(db_.get())); | |
501 for (size_t i = 0; i < kTableCount; ++i) { | |
502 DCHECK(db_->DoesTableExist(kTables[i].table_name)); | |
503 } | |
504 #endif | |
505 | |
506 return true; | |
507 } | |
508 | |
509 // static | |
510 bool QuotaDatabase::CreateSchema( | |
511 sql::Connection* database, | |
512 sql::MetaTable* meta_table, | |
513 int schema_version, int compatible_version, | |
514 const TableSchema* tables, size_t tables_size, | |
515 const IndexSchema* indexes, size_t indexes_size) { | |
516 // TODO(kinuko): Factor out the common code to create databases. | |
517 sql::Transaction transaction(database); | |
518 if (!transaction.Begin()) | |
519 return false; | |
520 | |
521 if (!meta_table->Init(database, schema_version, compatible_version)) | |
522 return false; | |
523 | |
524 for (size_t i = 0; i < tables_size; ++i) { | |
525 std::string sql("CREATE TABLE "); | |
526 sql += tables[i].table_name; | |
527 sql += tables[i].columns; | |
528 if (!database->Execute(sql.c_str())) { | |
529 VLOG(1) << "Failed to execute " << sql; | |
530 return false; | |
531 } | |
532 } | |
533 | |
534 for (size_t i = 0; i < indexes_size; ++i) { | |
535 std::string sql; | |
536 if (indexes[i].unique) | |
537 sql += "CREATE UNIQUE INDEX "; | |
538 else | |
539 sql += "CREATE INDEX "; | |
540 sql += indexes[i].index_name; | |
541 sql += " ON "; | |
542 sql += indexes[i].table_name; | |
543 sql += indexes[i].columns; | |
544 if (!database->Execute(sql.c_str())) { | |
545 VLOG(1) << "Failed to execute " << sql; | |
546 return false; | |
547 } | |
548 } | |
549 | |
550 return transaction.Commit(); | |
551 } | |
552 | |
553 bool QuotaDatabase::ResetSchema() { | |
554 DCHECK(!db_file_path_.empty()); | |
555 DCHECK(file_util::PathExists(db_file_path_)); | |
556 VLOG(1) << "Deleting existing quota data and starting over."; | |
557 | |
558 db_.reset(); | |
559 meta_table_.reset(); | |
560 | |
561 if (!file_util::Delete(db_file_path_, true)) | |
562 return false; | |
563 | |
564 // Make sure the steps above actually deleted things. | |
565 if (file_util::PathExists(db_file_path_)) | |
566 return false; | |
567 | |
568 // So we can't go recursive. | |
569 if (is_recreating_) | |
570 return false; | |
571 | |
572 base::AutoReset<bool> auto_reset(&is_recreating_, true); | |
573 return LazyOpen(true); | |
574 } | |
575 | |
576 bool QuotaDatabase::UpgradeSchema(int current_version) { | |
577 if (current_version == 2) { | |
578 QuotaTableImporter importer; | |
579 typedef std::vector<QuotaTableEntry> QuotaTableEntries; | |
580 if (!DumpQuotaTable(new QuotaTableCallback(base::Bind( | |
581 &QuotaTableImporter::Append, base::Unretained(&importer))))) | |
582 return false; | |
583 ResetSchema(); | |
584 for (QuotaTableEntries::const_iterator iter = importer.entries.begin(); | |
585 iter != importer.entries.end(); ++iter) { | |
586 if (!SetHostQuota(iter->host, iter->type, iter->quota)) | |
587 return false; | |
588 } | |
589 Commit(); | |
590 return true; | |
591 } | |
592 return false; | |
593 } | |
594 | |
595 bool QuotaDatabase::DumpQuotaTable(QuotaTableCallback* callback) { | |
596 scoped_ptr<QuotaTableCallback> callback_deleter(callback); | |
597 if (!LazyOpen(true)) | |
598 return false; | |
599 | |
600 const char* kSql = "SELECT * FROM HostQuotaTable"; | |
601 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
602 | |
603 while (statement.Step()) { | |
604 QuotaTableEntry entry = QuotaTableEntry( | |
605 statement.ColumnString(0), | |
606 static_cast<StorageType>(statement.ColumnInt(1)), | |
607 statement.ColumnInt64(2)); | |
608 | |
609 if (!callback->Run(entry)) | |
610 return true; | |
611 } | |
612 | |
613 return statement.Succeeded(); | |
614 } | |
615 | |
616 bool QuotaDatabase::DumpOriginInfoTable( | |
617 OriginInfoTableCallback* callback) { | |
618 scoped_ptr<OriginInfoTableCallback> callback_deleter(callback); | |
619 | |
620 if (!LazyOpen(true)) | |
621 return false; | |
622 | |
623 const char* kSql = "SELECT * FROM OriginInfoTable"; | |
624 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
625 | |
626 while (statement.Step()) { | |
627 OriginInfoTableEntry entry( | |
628 GURL(statement.ColumnString(0)), | |
629 static_cast<StorageType>(statement.ColumnInt(1)), | |
630 statement.ColumnInt(2), | |
631 base::Time::FromInternalValue(statement.ColumnInt64(3)), | |
632 base::Time::FromInternalValue(statement.ColumnInt64(4))); | |
633 | |
634 if (!callback->Run(entry)) | |
635 return true; | |
636 } | |
637 | |
638 return statement.Succeeded(); | |
639 } | |
640 | |
641 bool operator<(const QuotaDatabase::QuotaTableEntry& lhs, | |
642 const QuotaDatabase::QuotaTableEntry& rhs) { | |
643 if (lhs.host < rhs.host) return true; | |
644 if (rhs.host < lhs.host) return false; | |
645 if (lhs.type < rhs.type) return true; | |
646 if (rhs.type < lhs.type) return false; | |
647 return lhs.quota < rhs.quota; | |
648 } | |
649 | |
650 bool operator<(const QuotaDatabase::OriginInfoTableEntry& lhs, | |
651 const QuotaDatabase::OriginInfoTableEntry& rhs) { | |
652 if (lhs.origin < rhs.origin) return true; | |
653 if (rhs.origin < lhs.origin) return false; | |
654 if (lhs.type < rhs.type) return true; | |
655 if (rhs.type < lhs.type) return false; | |
656 if (lhs.used_count < rhs.used_count) return true; | |
657 if (rhs.used_count < lhs.used_count) return false; | |
658 return lhs.last_access_time < rhs.last_access_time; | |
659 } | |
660 | |
661 } // quota namespace | |
OLD | NEW |