Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1598)

Side by Side Diff: chrome/browser/history/top_sites_database.cc

Issue 560543002: [Top Sites] Encoding redirects field in TopSitesDatabase, and adding validations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Setting database to v4, adding migration code. Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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 "chrome/browser/history/top_sites_database.h" 5 #include "chrome/browser/history/top_sites_database.h"
6 6
7 #include "base/files/file_util.h" 7 #include "base/files/file_util.h"
8 #include "base/memory/ref_counted.h" 8 #include "base/memory/ref_counted.h"
9 #include "base/metrics/histogram.h" 9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_split.h" 10 #include "base/strings/string_split.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "chrome/browser/history/history_types.h" 12 #include "chrome/browser/history/history_types.h"
13 #include "chrome/browser/history/top_sites.h" 13 #include "chrome/browser/history/top_sites.h"
14 #include "components/history/core/common/thumbnail_score.h" 14 #include "components/history/core/common/thumbnail_score.h"
15 #include "sql/connection.h" 15 #include "sql/connection.h"
16 #include "sql/recovery.h" 16 #include "sql/recovery.h"
17 #include "sql/statement.h" 17 #include "sql/statement.h"
18 #include "sql/transaction.h" 18 #include "sql/transaction.h"
19 #include "third_party/sqlite/sqlite3.h" 19 #include "third_party/sqlite/sqlite3.h"
20 20
21 // Description of database table: 21 // Description of database table:
22 // 22 //
23 // thumbnails 23 // thumbnails
24 // url URL of the sites for which we have a thumbnail. 24 // url URL of the sites for which we have a thumbnail.
25 // url_rank Index of the URL in that thumbnail, 0-based. The thumbnail 25 // url_rank Index of the URL in that thumbnail, 0-based. The thumbnail
26 // with the highest rank will be the next one evicted. Forced 26 // with the highest rank will be the next one evicted. Forced
27 // thumbnails have a rank of -1. 27 // thumbnails have a rank of -1.
28 // title The title to display under that thumbnail. 28 // title The title to display under that thumbnail.
29 // redirects A space separated list of URLs that are known to redirect 29 // redirects A comma-separated list of URLs that are known to redirect
30 // to this url. 30 // to this url. Each URL is surrounded by quotes, and any
31 // existing quote is escaped to two quotes.
31 // boring_score How "boring" that thumbnail is. See ThumbnailScore. 32 // boring_score How "boring" that thumbnail is. See ThumbnailScore.
32 // good_clipping True if the thumbnail was clipped from the bottom, keeping 33 // good_clipping True if the thumbnail was clipped from the bottom, keeping
33 // the entire width of the window. See ThumbnailScore. 34 // the entire width of the window. See ThumbnailScore.
34 // at_top True if the thumbnail was captured at the top of the 35 // at_top True if the thumbnail was captured at the top of the
35 // website. 36 // website.
36 // last_updated The time at which this thumbnail was last updated. 37 // last_updated The time at which this thumbnail was last updated.
37 // load_completed True if the thumbnail was captured after the page load was 38 // load_completed True if the thumbnail was captured after the page load was
38 // completed. 39 // completed.
39 // last_forced If this is a forced thumbnail, records the last time it 40 // last_forced If this is a forced thumbnail, records the last time it
40 // was forced. If it's not a forced thumbnail, 0. 41 // was forced. If it's not a forced thumbnail, 0.
41 42
42 namespace { 43 namespace {
43 44
44 // For this database, schema migrations are deprecated after two 45 // For this database, schema migrations are deprecated after two
45 // years. This means that the oldest non-deprecated version should be 46 // years. This means that the oldest non-deprecated version should be
46 // two years old or greater (thus the migrations to get there are 47 // two years old or greater (thus the migrations to get there are
47 // older). Databases containing deprecated versions will be cleared 48 // older). Databases containing deprecated versions will be cleared
48 // at startup. Since this database is a cache, losing old data is not 49 // at startup. Since this database is a cache, losing old data is not
49 // fatal (in fact, very old data may be expired immediately at startup 50 // fatal (in fact, very old data may be expired immediately at startup
50 // anyhow). 51 // anyhow).
51 52
53 // Version 4: by huangs@chromium.org
52 // Version 3: b6d6a783/r231648 by beaudoin@chromium.org on 2013-10-29 54 // Version 3: b6d6a783/r231648 by beaudoin@chromium.org on 2013-10-29
53 // Version 2: eb0b24e6/r87284 by satorux@chromium.org on 2011-05-31 55 // Version 2: eb0b24e6/r87284 by satorux@chromium.org on 2011-05-31
54 // Version 1: 809cc4d8/r64072 by sky@chromium.org on 2010-10-27 (deprecated) 56 // Version 1: 809cc4d8/r64072 by sky@chromium.org on 2010-10-27 (deprecated)
55 57
56 // NOTE(shess): When changing the version, add a new golden file for 58 // NOTE(shess): When changing the version, add a new golden file for
57 // the new version and a test to verify that Init() works with it. 59 // the new version and a test to verify that Init() works with it.
58 // NOTE(shess): RecoverDatabaseOrRaze() depends on the specific 60 // NOTE(shess): RecoverDatabaseOrRaze() depends on the specific
59 // version number. The code is subtle and in development, contact me 61 // version number. The code is subtle and in development, contact me
60 // if the necessary changes are not obvious. 62 // if the necessary changes are not obvious.
61 static const int kVersionNumber = 3; 63 static const int kVersionNumber = 4;
62 static const int kDeprecatedVersionNumber = 1; // and earlier. 64 static const int kDeprecatedVersionNumber = 1; // and earlier.
63 65
64 bool InitTables(sql::Connection* db) { 66 bool InitTables(sql::Connection* db) {
65 const char kThumbnailsSql[] = 67 const char kThumbnailsSql[] =
66 "CREATE TABLE IF NOT EXISTS thumbnails (" 68 "CREATE TABLE IF NOT EXISTS thumbnails ("
67 "url LONGVARCHAR PRIMARY KEY," 69 "url LONGVARCHAR PRIMARY KEY,"
68 "url_rank INTEGER," 70 "url_rank INTEGER,"
69 "title LONGVARCHAR," 71 "title LONGVARCHAR,"
70 "thumbnail BLOB," 72 "thumbnail BLOB,"
71 "redirects LONGVARCHAR," 73 "redirects LONGVARCHAR,"
72 "boring_score DOUBLE DEFAULT 1.0," 74 "boring_score DOUBLE DEFAULT 1.0,"
73 "good_clipping INTEGER DEFAULT 0," 75 "good_clipping INTEGER DEFAULT 0,"
74 "at_top INTEGER DEFAULT 0," 76 "at_top INTEGER DEFAULT 0,"
75 "last_updated INTEGER DEFAULT 0," 77 "last_updated INTEGER DEFAULT 0,"
76 "load_completed INTEGER DEFAULT 0," 78 "load_completed INTEGER DEFAULT 0,"
77 "last_forced INTEGER DEFAULT 0)"; 79 "last_forced INTEGER DEFAULT 0)";
78 return db->Execute(kThumbnailsSql); 80 return db->Execute(kThumbnailsSql);
79 } 81 }
80 82
81 // Encodes redirects into a string.
82 std::string GetRedirects(const history::MostVisitedURL& url) {
83 std::vector<std::string> redirects;
84 for (size_t i = 0; i < url.redirects.size(); i++)
85 redirects.push_back(url.redirects[i].spec());
86 return JoinString(redirects, ' ');
87 }
88
89 // Decodes redirects from a string and sets them for the url.
90 void SetRedirects(const std::string& redirects, history::MostVisitedURL* url) {
91 std::vector<std::string> redirects_vector;
92 base::SplitStringAlongWhitespace(redirects, &redirects_vector);
93 for (size_t i = 0; i < redirects_vector.size(); ++i)
94 url->redirects.push_back(GURL(redirects_vector[i]));
95 }
96
97 // Track various failure (and success) cases in recovery code. 83 // Track various failure (and success) cases in recovery code.
98 // 84 //
99 // TODO(shess): The recovery code is complete, but by nature runs in challenging 85 // TODO(shess): The recovery code is complete, but by nature runs in challenging
100 // circumstances, so initially the default error response is to leave the 86 // circumstances, so initially the default error response is to leave the
101 // existing database in place. This histogram is intended to expose the 87 // existing database in place. This histogram is intended to expose the
102 // failures seen in the fleet. Frequent failure cases can be explored more 88 // failures seen in the fleet. Frequent failure cases can be explored more
103 // deeply to see if the complexity to fix them is warranted. Infrequent failure 89 // deeply to see if the complexity to fix them is warranted. Infrequent failure
104 // cases can be resolved by marking the database unrecoverable (which will 90 // cases can be resolved by marking the database unrecoverable (which will
105 // delete the data). 91 // delete the data).
106 // 92 //
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 ++next_rank; 175 ++next_rank;
190 } 176 }
191 if (adjusted) 177 if (adjusted)
192 RecordRecoveryEvent(RECOVERY_EVENT_INVARIANT_CONTIGUOUS); 178 RecordRecoveryEvent(RECOVERY_EVENT_INVARIANT_CONTIGUOUS);
193 } 179 }
194 180
195 // Recover the database to the extent possible, razing it if recovery is not 181 // Recover the database to the extent possible, razing it if recovery is not
196 // possible. 182 // possible.
197 void RecoverDatabaseOrRaze(sql::Connection* db, const base::FilePath& db_path) { 183 void RecoverDatabaseOrRaze(sql::Connection* db, const base::FilePath& db_path) {
198 // NOTE(shess): If the version changes, review this code. 184 // NOTE(shess): If the version changes, review this code.
199 DCHECK_EQ(3, kVersionNumber); 185 DCHECK_EQ(4, kVersionNumber);
200 186
201 // It is almost certain that some operation against |db| will fail, prevent 187 // It is almost certain that some operation against |db| will fail, prevent
202 // reentry. 188 // reentry.
203 db->reset_error_callback(); 189 db->reset_error_callback();
204 190
205 // For generating histogram stats. 191 // For generating histogram stats.
206 size_t thumbnails_recovered = 0; 192 size_t thumbnails_recovered = 0;
207 int64 original_size = 0; 193 int64 original_size = 0;
208 base::GetFileSize(db_path, &original_size); 194 base::GetFileSize(db_path, &original_size);
209 195
(...skipping 20 matching lines...) Expand all
230 // to raze the database. 216 // to raze the database.
231 if (version <= kDeprecatedVersionNumber) { 217 if (version <= kDeprecatedVersionNumber) {
232 sql::Recovery::Unrecoverable(recovery.Pass()); 218 sql::Recovery::Unrecoverable(recovery.Pass());
233 RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED); 219 RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED);
234 return; 220 return;
235 } 221 }
236 222
237 // TODO(shess): Earlier versions have been deprecated, later versions should 223 // TODO(shess): Earlier versions have been deprecated, later versions should
238 // be impossible. Unrecoverable() seems like a feasible response if this is 224 // be impossible. Unrecoverable() seems like a feasible response if this is
239 // infrequent enough. 225 // infrequent enough.
240 if (version != 2 && version != 3) { 226 if (version != 2 && version != 3 && version != 4) {
241 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION); 227 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION);
242 sql::Recovery::Rollback(recovery.Pass()); 228 sql::Recovery::Rollback(recovery.Pass());
243 return; 229 return;
244 } 230 }
245 231
246 // Both v2 and v3 recover to current schema version. 232 // v2, v3, and v4 recover to current schema version.
247 sql::MetaTable recover_meta_table; 233 sql::MetaTable recover_meta_table;
248 if (!recover_meta_table.Init(recovery->db(), kVersionNumber, 234 if (!recover_meta_table.Init(recovery->db(), kVersionNumber,
249 kVersionNumber)) { 235 kVersionNumber)) {
250 sql::Recovery::Rollback(recovery.Pass()); 236 sql::Recovery::Rollback(recovery.Pass());
251 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT); 237 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT);
252 return; 238 return;
253 } 239 }
254 240
255 // Create a fresh version of the schema. The recovery code uses 241 // Create a fresh version of the schema. The recovery code uses
256 // conflict-resolution to handle duplicates, so any indices are necessary. 242 // conflict-resolution to handle duplicates, so any indices are necessary.
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 if (!InitTables(db_.get())) 392 if (!InitTables(db_.get()))
407 return false; 393 return false;
408 394
409 if (meta_table_.GetVersionNumber() == 2) { 395 if (meta_table_.GetVersionNumber() == 2) {
410 if (!UpgradeToVersion3()) { 396 if (!UpgradeToVersion3()) {
411 LOG(WARNING) << "Unable to upgrade top sites database to version 3."; 397 LOG(WARNING) << "Unable to upgrade top sites database to version 3.";
412 return false; 398 return false;
413 } 399 }
414 } 400 }
415 401
402 if (meta_table_.GetVersionNumber() == 3) {
403 if (!UpgradeToVersion4()) {
404 LOG(WARNING) << "Unable to upgrade top sites database to version 4.";
405 return false;
406 }
407 }
408
416 // Version check. 409 // Version check.
417 if (meta_table_.GetVersionNumber() != kVersionNumber) 410 if (meta_table_.GetVersionNumber() != kVersionNumber)
418 return false; 411 return false;
419 412
420 // Initialization is complete. 413 // Initialization is complete.
421 if (!transaction.Commit()) 414 if (!transaction.Commit())
422 return false; 415 return false;
423 416
424 return true; 417 return true;
425 } 418 }
426 419
427 bool TopSitesDatabase::UpgradeToVersion3() { 420 bool TopSitesDatabase::UpgradeToVersion3() {
428 // Add 'last_forced' column. 421 // Add 'last_forced' column.
429 if (!db_->Execute( 422 if (!db_->Execute(
430 "ALTER TABLE thumbnails ADD last_forced INTEGER DEFAULT 0")) { 423 "ALTER TABLE thumbnails ADD last_forced INTEGER DEFAULT 0")) {
431 NOTREACHED(); 424 NOTREACHED();
432 return false; 425 return false;
433 } 426 }
434 meta_table_.SetVersionNumber(3); 427 meta_table_.SetVersionNumber(3);
435 return true; 428 return true;
436 } 429 }
437 430
431 bool TopSitesDatabase::UpgradeToVersion4() {
432 LOG(ERROR) << "Migrating to V4";
433
434 // Migrate the "redirects" field from space-separated list in v3 to
435 // comma-separated list of quoted strings in v4.
436 const char kSelectSql[] = "SELECT redirects, rowid FROM thumbnails";
437 sql::Statement select_statement(db_->GetUniqueStatement(kSelectSql));
438
439 const char kUpdateSql[] =
440 "UPDATE thumbnails SET redirects = ? WHERE rowid = ?";
441 sql::Statement update_statement(db_->GetUniqueStatement(kUpdateSql));
442
443 while (select_statement.Step()) {
444 const std::string encoded_redirects_v3(select_statement.ColumnString(0));
445
446 LOG(ERROR) << "Old: " << encoded_redirects_v3;
447 // Skip if empty or if previously migrated
448 if (encoded_redirects_v3.empty() || encoded_redirects_v3[0] == '"')
449 continue;
450
451 // Parse v3 redirects, which is a space-separated list of URLs.
452 std::vector<std::string> redirects_vector;
453 RedirectList redirects;
454 base::SplitStringAlongWhitespace(encoded_redirects_v3, &redirects_vector);
455 for (size_t i = 0; i < redirects_vector.size(); ++i) {
456 GURL redirect_url(redirects_vector[i]);
457 if (redirect_url.is_valid())
458 redirects.push_back(redirect_url);
459 }
460
461 std::string encoded_redirects(EncodeRedirects(redirects));
462 LOG(ERROR) << "New: " << encoded_redirects;
463
464 update_statement.Reset(true);
465 update_statement.BindString(0, encoded_redirects);
466 update_statement.BindInt64(1, select_statement.ColumnInt64(1));
467 update_statement.Run();
468 }
469
470 meta_table_.SetVersionNumber(4);
471 return true;
472 }
473
438 void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls, 474 void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls,
439 URLToImagesMap* thumbnails) { 475 URLToImagesMap* thumbnails) {
440 sql::Statement statement(db_->GetCachedStatement( 476 sql::Statement statement(db_->GetCachedStatement(
441 SQL_FROM_HERE, 477 SQL_FROM_HERE,
442 "SELECT url, url_rank, title, thumbnail, redirects, " 478 "SELECT url, url_rank, title, thumbnail, redirects, "
443 "boring_score, good_clipping, at_top, last_updated, load_completed, " 479 "boring_score, good_clipping, at_top, last_updated, load_completed, "
444 "last_forced FROM thumbnails ORDER BY url_rank, last_forced")); 480 "last_forced FROM thumbnails ORDER BY url_rank, last_forced"));
445 481
446 if (!statement.is_valid()) { 482 if (!statement.is_valid()) {
447 LOG(WARNING) << db_->GetErrorMessage(); 483 LOG(WARNING) << db_->GetErrorMessage();
448 return; 484 return;
449 } 485 }
450 486
451 urls->clear(); 487 urls->clear();
452 thumbnails->clear(); 488 thumbnails->clear();
453 489
454 while (statement.Step()) { 490 while (statement.Step()) {
455 // Results are sorted by url_rank. For forced thumbnails with url_rank = -1, 491 // Results are sorted by url_rank. For forced thumbnails with url_rank = -1,
456 // thumbnails are sorted by last_forced. 492 // thumbnails are sorted by last_forced.
457 MostVisitedURL url; 493 MostVisitedURL url;
458 GURL gurl(statement.ColumnString(0)); 494 GURL gurl(statement.ColumnString(0));
459 url.url = gurl; 495 url.url = gurl;
460 url.title = statement.ColumnString16(2); 496 url.title = statement.ColumnString16(2);
461 url.last_forced_time = 497 url.last_forced_time =
462 base::Time::FromInternalValue(statement.ColumnInt64(10)); 498 base::Time::FromInternalValue(statement.ColumnInt64(10));
463 std::string redirects = statement.ColumnString(4); 499 LOG(ERROR) << "Read db: redirects = " << statement.ColumnString(4);
464 SetRedirects(redirects, &url); 500 std::string encoded_redirects = statement.ColumnString(4);
501 DecodeRedirects(encoded_redirects, &url.redirects);
465 urls->push_back(url); 502 urls->push_back(url);
466 503
467 std::vector<unsigned char> data; 504 std::vector<unsigned char> data;
468 statement.ColumnBlobAsVector(3, &data); 505 statement.ColumnBlobAsVector(3, &data);
469 Images thumbnail; 506 Images thumbnail;
470 if (!data.empty()) 507 if (!data.empty())
471 thumbnail.thumbnail = base::RefCountedBytes::TakeVector(&data); 508 thumbnail.thumbnail = base::RefCountedBytes::TakeVector(&data);
472 thumbnail.thumbnail_score.boring_score = statement.ColumnDouble(5); 509 thumbnail.thumbnail_score.boring_score = statement.ColumnDouble(5);
473 thumbnail.thumbnail_score.good_clipping = statement.ColumnBool(6); 510 thumbnail.thumbnail_score.good_clipping = statement.ColumnBool(6);
474 thumbnail.thumbnail_score.at_top = statement.ColumnBool(7); 511 thumbnail.thumbnail_score.at_top = statement.ColumnBool(7);
(...skipping 28 matching lines...) Expand all
503 "UPDATE thumbnails SET " 540 "UPDATE thumbnails SET "
504 "title = ?, thumbnail = ?, redirects = ?, " 541 "title = ?, thumbnail = ?, redirects = ?, "
505 "boring_score = ?, good_clipping = ?, at_top = ?, last_updated = ?, " 542 "boring_score = ?, good_clipping = ?, at_top = ?, last_updated = ?, "
506 "load_completed = ?, last_forced = ?" 543 "load_completed = ?, last_forced = ?"
507 "WHERE url = ? ")); 544 "WHERE url = ? "));
508 statement.BindString16(0, url.title); 545 statement.BindString16(0, url.title);
509 if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { 546 if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) {
510 statement.BindBlob(1, thumbnail.thumbnail->front(), 547 statement.BindBlob(1, thumbnail.thumbnail->front(),
511 static_cast<int>(thumbnail.thumbnail->size())); 548 static_cast<int>(thumbnail.thumbnail->size()));
512 } 549 }
513 statement.BindString(2, GetRedirects(url)); 550 statement.BindString(2, EncodeRedirects(url.redirects));
514 const ThumbnailScore& score = thumbnail.thumbnail_score; 551 const ThumbnailScore& score = thumbnail.thumbnail_score;
515 statement.BindDouble(3, score.boring_score); 552 statement.BindDouble(3, score.boring_score);
516 statement.BindBool(4, score.good_clipping); 553 statement.BindBool(4, score.good_clipping);
517 statement.BindBool(5, score.at_top); 554 statement.BindBool(5, score.at_top);
518 statement.BindInt64(6, score.time_at_snapshot.ToInternalValue()); 555 statement.BindInt64(6, score.time_at_snapshot.ToInternalValue());
519 statement.BindBool(7, score.load_completed); 556 statement.BindBool(7, score.load_completed);
520 statement.BindInt64(8, url.last_forced_time.ToInternalValue()); 557 statement.BindInt64(8, url.last_forced_time.ToInternalValue());
521 statement.BindString(9, url.url.spec()); 558 statement.BindString(9, url.url.spec());
522 559
523 return statement.Run(); 560 return statement.Run();
524 } 561 }
525 562
526 void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url, 563 void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url,
527 int new_rank, 564 int new_rank,
528 const Images& thumbnail) { 565 const Images& thumbnail) {
529 sql::Statement statement(db_->GetCachedStatement( 566 sql::Statement statement(db_->GetCachedStatement(
530 SQL_FROM_HERE, 567 SQL_FROM_HERE,
531 "INSERT OR REPLACE INTO thumbnails " 568 "INSERT OR REPLACE INTO thumbnails "
532 "(url, url_rank, title, thumbnail, redirects, " 569 "(url, url_rank, title, thumbnail, redirects, "
533 "boring_score, good_clipping, at_top, last_updated, load_completed, " 570 "boring_score, good_clipping, at_top, last_updated, load_completed, "
534 "last_forced) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); 571 "last_forced) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
535 statement.BindString(0, url.url.spec()); 572 statement.BindString(0, url.url.spec());
536 statement.BindInt(1, kRankOfForcedURL); // Fist make it a forced thumbnail. 573 statement.BindInt(1, kRankOfForcedURL); // Fist make it a forced thumbnail.
537 statement.BindString16(2, url.title); 574 statement.BindString16(2, url.title);
538 if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { 575 if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) {
539 statement.BindBlob(3, thumbnail.thumbnail->front(), 576 statement.BindBlob(3, thumbnail.thumbnail->front(),
540 static_cast<int>(thumbnail.thumbnail->size())); 577 static_cast<int>(thumbnail.thumbnail->size()));
541 } 578 }
542 statement.BindString(4, GetRedirects(url)); 579 statement.BindString(4, EncodeRedirects(url.redirects));
543 const ThumbnailScore& score = thumbnail.thumbnail_score; 580 const ThumbnailScore& score = thumbnail.thumbnail_score;
544 statement.BindDouble(5, score.boring_score); 581 statement.BindDouble(5, score.boring_score);
545 statement.BindBool(6, score.good_clipping); 582 statement.BindBool(6, score.good_clipping);
546 statement.BindBool(7, score.at_top); 583 statement.BindBool(7, score.at_top);
547 statement.BindInt64(8, score.time_at_snapshot.ToInternalValue()); 584 statement.BindInt64(8, score.time_at_snapshot.ToInternalValue());
548 statement.BindBool(9, score.load_completed); 585 statement.BindBool(9, score.load_completed);
549 int64 last_forced = url.last_forced_time.ToInternalValue(); 586 int64 last_forced = url.last_forced_time.ToInternalValue();
550 DCHECK((last_forced == 0) == (new_rank != kRankOfForcedURL)) 587 DCHECK((last_forced == 0) == (new_rank != kRankOfForcedURL))
551 << "Thumbnail without a forced time stamp has a forced rank, or the " 588 << "Thumbnail without a forced time stamp has a forced rank, or the "
552 << "opposite."; 589 << "opposite.";
553 statement.BindInt64(10, last_forced); 590 statement.BindInt64(10, last_forced);
554 if (!statement.Run()) 591 if (!statement.Run())
555 return; 592 return;
556 593
557 // Update rank if this is not a forced thumbnail. 594 // Update rank if this is not a forced thumbnail.
558 if (new_rank != kRankOfForcedURL) 595 if (new_rank != kRankOfForcedURL)
559 UpdatePageRankNoTransaction(url, new_rank); 596 UpdatePageRankNoTransaction(url, new_rank);
560 } 597 }
561 598
599 // static
600 std::string TopSitesDatabase::EncodeCSVString(
601 const std::vector<std::string> str_list) {
602 std::string csv;
603 for (std::vector<std::string>::const_iterator it = str_list.begin();
604 it != str_list.end(); ++it) {
605 const std::string& str = *it;
606 if (it != str_list.begin())
607 csv += ',';
608 csv += '"';
609 for (std::string::const_iterator jt = str.begin(); jt != str.end(); ++jt) {
610 if (*jt == '"')
611 csv += '"';
612 csv += *jt;
613 }
614 csv += '"';
615 }
616 return csv;
617 }
618
619 // static
620 bool TopSitesDatabase::DecodeCSVString(const std::string csv,
621 std::vector<std::string>* str_list) {
622 if (csv.empty()) {
623 str_list->clear();
624 return true;
625 }
626
627 enum {
628 SEEK_QUOTE,
629 READ_STRING,
630 SAW_ONE_QUOTE
631 } state = SEEK_QUOTE;
632 std::vector<std::string> out_list;
633 std::string str;
634 for (std::string::const_iterator it = csv.begin(); it != csv.end(); ++it) {
635 const char ch = *it;
636 if (state == SEEK_QUOTE) {
637 if (ch != '"')
638 return false;
639 state = READ_STRING;
640 } else if (state == READ_STRING) {
641 if (ch == '"')
642 state = SAW_ONE_QUOTE;
643 else
644 str += ch;
645 } else if (state == SAW_ONE_QUOTE) {
646 if (ch == '"') {
647 str += ch;
648 state = READ_STRING;
649 } else if (ch == ',') {
650 out_list.push_back(str);
651 str.clear();
652 // For simplicity, disallow spaces around commas.
653 state = SEEK_QUOTE;
654 } else {
655 return false;
656 }
657 } else {
658 NOTREACHED();
659 return false;
660 }
661 }
662 if (state != SAW_ONE_QUOTE)
663 return false;
664 out_list.push_back(str);
665 str_list->swap(out_list);
666 return true;
667 }
668
669 // static
670 std::string TopSitesDatabase::EncodeRedirects(const RedirectList& redirects) {
671 std::vector<std::string> valid_urls;
672 for (size_t i = 0; i < redirects.size(); i++) {
673 // Example of invalid URL that may end up here:
674 // "data:text/plain,this string contains space".
675 if (redirects[i].is_valid())
676 valid_urls.push_back(redirects[i].spec());
677 }
678 return EncodeCSVString(valid_urls);
679 }
680
681 // static
682 void TopSitesDatabase::DecodeRedirects(const std::string& encoded_redirects,
683 RedirectList* redirects) {
684 std::vector<std::string> redirects_vector;
685 if (!DecodeCSVString(encoded_redirects, &redirects_vector)) {
686 // Fall back to space-delimited list for backward compatibility.
687 base::SplitStringAlongWhitespace(encoded_redirects, &redirects_vector);
688 }
689 for (size_t i = 0; i < redirects_vector.size(); ++i) {
690 GURL redirect_url(redirects_vector[i]);
691 if (redirect_url.is_valid())
692 redirects->push_back(redirect_url);
693 }
694 }
695
562 void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url, 696 void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url,
563 int new_rank) { 697 int new_rank) {
564 DCHECK((url.last_forced_time.ToInternalValue() == 0) == 698 DCHECK((url.last_forced_time.ToInternalValue() == 0) ==
565 (new_rank != kRankOfForcedURL)) 699 (new_rank != kRankOfForcedURL))
566 << "Thumbnail without a forced time stamp has a forced rank, or the " 700 << "Thumbnail without a forced time stamp has a forced rank, or the "
567 << "opposite."; 701 << "opposite.";
568 sql::Transaction transaction(db_.get()); 702 sql::Transaction transaction(db_.get());
569 transaction.Begin(); 703 transaction.Begin();
570 UpdatePageRankNoTransaction(url, new_rank); 704 UpdatePageRankNoTransaction(url, new_rank);
571 transaction.Commit(); 705 transaction.Commit();
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
724 db.get(), db_name)); 858 db.get(), db_name));
725 db->set_page_size(4096); 859 db->set_page_size(4096);
726 db->set_cache_size(32); 860 db->set_cache_size(32);
727 861
728 if (!db->Open(db_name)) 862 if (!db->Open(db_name))
729 return NULL; 863 return NULL;
730 return db.release(); 864 return db.release();
731 } 865 }
732 866
733 } // namespace history 867 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/history/top_sites_database.h ('k') | chrome/browser/history/top_sites_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698