Index: chrome/browser/history/top_sites_database.cc |
diff --git a/chrome/browser/history/top_sites_database.cc b/chrome/browser/history/top_sites_database.cc |
index 5c1939fbf6be059e23958c1ec0f3a806bc27cc26..db9156d4ea06c5d73abf992872868a401d7749a5 100644 |
--- a/chrome/browser/history/top_sites_database.cc |
+++ b/chrome/browser/history/top_sites_database.cc |
@@ -9,19 +9,44 @@ |
#include "chrome/browser/history/history_types.h" |
#include "chrome/browser/history/top_sites.h" |
#include "chrome/browser/history/top_sites_database.h" |
+#include "chrome/common/thumbnail_score.h" |
#include "sql/connection.h" |
#include "sql/transaction.h" |
+// Description of database table: |
+// |
+// thumbnails |
+// url URL of the sites for which we have a thumbnail. |
+// url_rank Index of the URL in that thumbnail, 0-based. The thumbnail |
+// with the highest rank will be the next one evicted. Forced |
+// thumbnails have a rank of -1. |
+// title The title to display under that thumbnail. |
+// redirects A space separated list of URLs that are known to redirect |
+// to this url. |
+// boring_score How "boring" that thumbnail is. See ThumbnailScore. |
+// good_clipping True if the thumbnail was clipped from the bottom, keeping |
+// the entire width of the window. See ThumbnailScore. |
+// at_top True if the thumbnail was captured at the top of the |
+// website. |
+// last_updated The time at which this thumbnail was last updated. |
+// load_completed True if the thumbnail was captured after the page load was |
+// completed. |
+// last_forced If this is a forced thumbnail, records the last time it |
+// was forced. If it's not a forced thumbnail, 0. |
+ |
namespace history { |
+// TODO(beaudoin): Fill revision/date details of Version 3 after landing. |
+// Version 3: by beaudoin@chromium.org |
// Version 2: eb0b24e6/r87284 by satorux@chromium.org on 2011-05-31 |
// Version 1: 809cc4d8/r64072 by sky@chromium.org on 2010-10-27 |
// From the version 1 to 2, one column was added. Old versions of Chrome |
-// should be able to read version 2 files just fine. |
+// should be able to read version 2 files just fine. Same thing for version 2 |
+// to 3. |
// NOTE(shess): When changing the version, add a new golden file for |
// the new version and a test to verify that Init() works with it. |
-static const int kVersionNumber = 2; |
+static const int kVersionNumber = 3; |
TopSitesDatabase::TopSitesDatabase() { |
} |
@@ -69,6 +94,13 @@ bool TopSitesDatabase::Init(const base::FilePath& db_name) { |
} |
} |
+ if (meta_table_.GetVersionNumber() == 2) { |
+ if (!UpgradeToVersion3()) { |
+ LOG(WARNING) << "Unable to upgrade top sites database to version 3."; |
+ return false; |
+ } |
+ } |
+ |
// Version check. |
if (meta_table_.GetVersionNumber() != kVersionNumber) |
return false; |
@@ -84,15 +116,16 @@ bool TopSitesDatabase::InitThumbnailTable() { |
if (!db_->DoesTableExist("thumbnails")) { |
if (!db_->Execute("CREATE TABLE thumbnails (" |
"url LONGVARCHAR PRIMARY KEY," |
- "url_rank INTEGER ," |
+ "url_rank INTEGER," |
"title LONGVARCHAR," |
"thumbnail BLOB," |
"redirects LONGVARCHAR," |
- "boring_score DOUBLE DEFAULT 1.0, " |
- "good_clipping INTEGER DEFAULT 0, " |
- "at_top INTEGER DEFAULT 0, " |
- "last_updated INTEGER DEFAULT 0, " |
- "load_completed INTEGER DEFAULT 0) ")) { |
+ "boring_score DOUBLE DEFAULT 1.0," |
+ "good_clipping INTEGER DEFAULT 0," |
+ "at_top INTEGER DEFAULT 0," |
+ "last_updated INTEGER DEFAULT 0," |
+ "load_completed INTEGER DEFAULT 0," |
+ "last_forced INTEGER DEFAULT 0)")) { |
LOG(WARNING) << db_->GetErrorMessage(); |
return false; |
} |
@@ -111,13 +144,24 @@ bool TopSitesDatabase::UpgradeToVersion2() { |
return true; |
} |
+bool TopSitesDatabase::UpgradeToVersion3() { |
+ // Add 'last_forced' column. |
+ if (!db_->Execute( |
+ "ALTER TABLE thumbnails ADD last_forced INTEGER DEFAULT 0")) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ meta_table_.SetVersionNumber(3); |
+ return true; |
+} |
+ |
void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls, |
URLToImagesMap* thumbnails) { |
sql::Statement statement(db_->GetCachedStatement( |
SQL_FROM_HERE, |
"SELECT url, url_rank, title, thumbnail, redirects, " |
- "boring_score, good_clipping, at_top, last_updated, load_completed " |
- "FROM thumbnails ORDER BY url_rank ")); |
+ "boring_score, good_clipping, at_top, last_updated, load_completed, " |
+ "last_forced FROM thumbnails ORDER BY url_rank, last_forced")); |
if (!statement.is_valid()) { |
LOG(WARNING) << db_->GetErrorMessage(); |
@@ -128,11 +172,14 @@ void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls, |
thumbnails->clear(); |
while (statement.Step()) { |
- // Results are sorted by url_rank. |
+ // Results are sorted by url_rank. For forced thumbnails with url_rank = -1, |
+ // thumbnails are sorted by last_forced. |
MostVisitedURL url; |
GURL gurl(statement.ColumnString(0)); |
url.url = gurl; |
url.title = statement.ColumnString16(2); |
+ url.last_forced_time = |
+ base::Time::FromInternalValue(statement.ColumnInt64(10)); |
std::string redirects = statement.ColumnString(4); |
SetRedirects(redirects, &url); |
urls->push_back(url); |
@@ -148,7 +195,6 @@ void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls, |
thumbnail.thumbnail_score.time_at_snapshot = |
base::Time::FromInternalValue(statement.ColumnInt64(8)); |
thumbnail.thumbnail_score.load_completed = statement.ColumnBool(9); |
- |
(*thumbnails)[gurl] = thumbnail; |
} |
} |
@@ -171,13 +217,13 @@ void TopSitesDatabase::SetRedirects(const std::string& redirects, |
} |
void TopSitesDatabase::SetPageThumbnail(const MostVisitedURL& url, |
- int new_rank, |
- const Images& thumbnail) { |
+ int new_rank, |
+ const Images& thumbnail) { |
sql::Transaction transaction(db_.get()); |
transaction.Begin(); |
int rank = GetURLRank(url); |
- if (rank == -1) { |
+ if (rank == kRankOfNonExistingURL) { |
AddPageThumbnail(url, new_rank, thumbnail); |
} else { |
UpdatePageRankNoTransaction(url, new_rank); |
@@ -194,7 +240,7 @@ bool TopSitesDatabase::UpdatePageThumbnail( |
"UPDATE thumbnails SET " |
"title = ?, thumbnail = ?, redirects = ?, " |
"boring_score = ?, good_clipping = ?, at_top = ?, last_updated = ?, " |
- "load_completed = ? " |
+ "load_completed = ?, last_forced = ?" |
"WHERE url = ? ")); |
statement.BindString16(0, url.title); |
if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { |
@@ -208,24 +254,23 @@ bool TopSitesDatabase::UpdatePageThumbnail( |
statement.BindBool(5, score.at_top); |
statement.BindInt64(6, score.time_at_snapshot.ToInternalValue()); |
statement.BindBool(7, score.load_completed); |
- statement.BindString(8, url.url.spec()); |
+ statement.BindInt64(8, url.last_forced_time.ToInternalValue()); |
+ statement.BindString(9, url.url.spec()); |
return statement.Run(); |
} |
void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url, |
- int new_rank, |
- const Images& thumbnail) { |
- int count = GetRowCount(); |
- |
+ int new_rank, |
+ const Images& thumbnail) { |
sql::Statement statement(db_->GetCachedStatement( |
SQL_FROM_HERE, |
"INSERT OR REPLACE INTO thumbnails " |
"(url, url_rank, title, thumbnail, redirects, " |
- "boring_score, good_clipping, at_top, last_updated, load_completed) " |
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
+ "boring_score, good_clipping, at_top, last_updated, load_completed, " |
+ "last_forced) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
statement.BindString(0, url.url.spec()); |
- statement.BindInt(1, count); // Make it the last url. |
+ statement.BindInt(1, kRankOfForcedURL); // Fist make it a forced thumbnail. |
statement.BindString16(2, url.title); |
if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { |
statement.BindBlob(3, thumbnail.thumbnail->front(), |
@@ -238,14 +283,25 @@ void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url, |
statement.BindBool(7, score.at_top); |
statement.BindInt64(8, score.time_at_snapshot.ToInternalValue()); |
statement.BindBool(9, score.load_completed); |
+ int64 last_forced = url.last_forced_time.ToInternalValue(); |
+ DCHECK((last_forced == 0) == (new_rank != kRankOfForcedURL)) |
+ << "Thumbnail without a forced time stamp has a forced rank, or the " |
+ << "opposite."; |
+ statement.BindInt64(10, last_forced); |
if (!statement.Run()) |
return; |
- UpdatePageRankNoTransaction(url, new_rank); |
+ // Update rank if this is not a forced thumbnail. |
+ if (new_rank != kRankOfForcedURL) |
+ UpdatePageRankNoTransaction(url, new_rank); |
} |
void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url, |
- int new_rank) { |
+ int new_rank) { |
+ DCHECK((url.last_forced_time.ToInternalValue() == 0) == |
+ (new_rank != kRankOfForcedURL)) |
+ << "Thumbnail without a forced time stamp has a forced rank, or the " |
+ << "opposite."; |
sql::Transaction transaction(db_.get()); |
transaction.Begin(); |
UpdatePageRankNoTransaction(url, new_rank); |
@@ -256,43 +312,76 @@ void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url, |
void TopSitesDatabase::UpdatePageRankNoTransaction( |
const MostVisitedURL& url, int new_rank) { |
DCHECK_GT(db_->transaction_nesting(), 0); |
+ |
int prev_rank = GetURLRank(url); |
- if (prev_rank == -1) { |
+ if (prev_rank == kRankOfNonExistingURL) { |
LOG(WARNING) << "Updating rank of an unknown URL: " << url.url.spec(); |
return; |
} |
// Shift the ranks. |
if (prev_rank > new_rank) { |
- // Shift up |
- sql::Statement shift_statement(db_->GetCachedStatement( |
- SQL_FROM_HERE, |
- "UPDATE thumbnails " |
- "SET url_rank = url_rank + 1 " |
- "WHERE url_rank >= ? AND url_rank < ?")); |
- shift_statement.BindInt(0, new_rank); |
- shift_statement.BindInt(1, prev_rank); |
- shift_statement.Run(); |
+ if (new_rank == kRankOfForcedURL) { |
+ // From non-forced to forced, shift down. |
+ // Example: 2 -> -1 |
+ // -1, -1, -1, 0, 1, [2 -> -1], [3 -> 2], [4 -> 3] |
+ sql::Statement shift_statement(db_->GetCachedStatement( |
+ SQL_FROM_HERE, |
+ "UPDATE thumbnails " |
+ "SET url_rank = url_rank - 1 " |
+ "WHERE url_rank > ?")); |
+ shift_statement.BindInt(0, prev_rank); |
+ shift_statement.Run(); |
+ } else { |
+ // From non-forced to non-forced, shift up. |
+ // Example: 3 -> 1 |
+ // -1, -1, -1, 0, [1 -> 2], [2 -> 3], [3 -> 1], 4 |
+ sql::Statement shift_statement(db_->GetCachedStatement( |
+ SQL_FROM_HERE, |
+ "UPDATE thumbnails " |
+ "SET url_rank = url_rank + 1 " |
+ "WHERE url_rank >= ? AND url_rank < ?")); |
+ shift_statement.BindInt(0, new_rank); |
+ shift_statement.BindInt(1, prev_rank); |
+ shift_statement.Run(); |
+ } |
} else if (prev_rank < new_rank) { |
- // Shift down |
- sql::Statement shift_statement(db_->GetCachedStatement( |
- SQL_FROM_HERE, |
- "UPDATE thumbnails " |
- "SET url_rank = url_rank - 1 " |
- "WHERE url_rank > ? AND url_rank <= ?")); |
- shift_statement.BindInt(0, prev_rank); |
- shift_statement.BindInt(1, new_rank); |
- shift_statement.Run(); |
+ if (prev_rank == kRankOfForcedURL) { |
+ // From non-forced to forced, shift up. |
+ // Example: -1 -> 2 |
+ // -1, [-1 -> 2], -1, 0, 1, [2 -> 3], [3 -> 4], [4 -> 5] |
+ sql::Statement shift_statement(db_->GetCachedStatement( |
+ SQL_FROM_HERE, |
+ "UPDATE thumbnails " |
+ "SET url_rank = url_rank + 1 " |
+ "WHERE url_rank >= ?")); |
+ shift_statement.BindInt(0, new_rank); |
+ shift_statement.Run(); |
+ } else { |
+ // From non-forced to non-forced, shift down. |
+ // Example: 1 -> 3. |
+ // -1, -1, -1, 0, [1 -> 3], [2 -> 1], [3 -> 2], 4 |
+ sql::Statement shift_statement(db_->GetCachedStatement( |
+ SQL_FROM_HERE, |
+ "UPDATE thumbnails " |
+ "SET url_rank = url_rank - 1 " |
+ "WHERE url_rank > ? AND url_rank <= ?")); |
+ shift_statement.BindInt(0, prev_rank); |
+ shift_statement.BindInt(1, new_rank); |
+ shift_statement.Run(); |
+ } |
} |
- // Set the url's rank. |
+ // Set the url's rank and last_forced, since the latter changes when a URL |
+ // goes from forced to non-forced and vice-versa. |
sql::Statement set_statement(db_->GetCachedStatement( |
SQL_FROM_HERE, |
"UPDATE thumbnails " |
- "SET url_rank = ? " |
+ "SET url_rank = ?, last_forced = ? " |
"WHERE url == ?")); |
set_statement.BindInt(0, new_rank); |
- set_statement.BindString(1, url.url.spec()); |
+ set_statement.BindInt64(1, url.last_forced_time.ToInternalValue()); |
+ set_statement.BindString(2, url.url.spec()); |
set_statement.Run(); |
} |
@@ -317,16 +406,6 @@ bool TopSitesDatabase::GetPageThumbnail(const GURL& url, |
return true; |
} |
-int TopSitesDatabase::GetRowCount() { |
- sql::Statement select_statement(db_->GetCachedStatement( |
- SQL_FROM_HERE, |
- "SELECT COUNT (url) FROM thumbnails")); |
- if (select_statement.Step()) |
- return select_statement.ColumnInt(0); |
- |
- return 0; |
-} |
- |
int TopSitesDatabase::GetURLRank(const MostVisitedURL& url) { |
sql::Statement select_statement(db_->GetCachedStatement( |
SQL_FROM_HERE, |
@@ -336,27 +415,29 @@ int TopSitesDatabase::GetURLRank(const MostVisitedURL& url) { |
if (select_statement.Step()) |
return select_statement.ColumnInt(0); |
- return -1; |
+ return kRankOfNonExistingURL; |
} |
// Remove the record for this URL. Returns true iff removed successfully. |
bool TopSitesDatabase::RemoveURL(const MostVisitedURL& url) { |
int old_rank = GetURLRank(url); |
- if (old_rank < 0) |
+ if (old_rank == kRankOfNonExistingURL) |
return false; |
sql::Transaction transaction(db_.get()); |
transaction.Begin(); |
- // Decrement all following ranks. |
- sql::Statement shift_statement(db_->GetCachedStatement( |
- SQL_FROM_HERE, |
- "UPDATE thumbnails " |
- "SET url_rank = url_rank - 1 " |
- "WHERE url_rank > ?")); |
- shift_statement.BindInt(0, old_rank); |
+ if (old_rank != kRankOfForcedURL) { |
+ // Decrement all following ranks. |
+ sql::Statement shift_statement(db_->GetCachedStatement( |
+ SQL_FROM_HERE, |
+ "UPDATE thumbnails " |
+ "SET url_rank = url_rank - 1 " |
+ "WHERE url_rank > ?")); |
+ shift_statement.BindInt(0, old_rank); |
- if (!shift_statement.Run()) |
- return false; |
+ if (!shift_statement.Run()) |
+ return false; |
+ } |
sql::Statement delete_statement( |
db_->GetCachedStatement(SQL_FROM_HERE, |