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

Side by Side Diff: components/enhanced_bookmarks/persistent_image_store.cc

Issue 875463003: ★ Record the image dominant color in the image database. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix GN Created 5 years, 11 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 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 "components/enhanced_bookmarks/persistent_image_store.h" 5 #include "components/enhanced_bookmarks/persistent_image_store.h"
6 6
7 #include "base/files/file.h" 7 #include "base/files/file.h"
8 #include "base/logging.h"
8 #include "components/enhanced_bookmarks/image_store_util.h" 9 #include "components/enhanced_bookmarks/image_store_util.h"
9 #include "sql/statement.h" 10 #include "sql/statement.h"
10 #include "sql/transaction.h" 11 #include "sql/transaction.h"
11 #include "ui/gfx/geometry/size.h" 12 #include "ui/gfx/geometry/size.h"
12 #include "url/gurl.h" 13 #include "url/gurl.h"
13 14
14 namespace { 15 namespace {
16 // Current version number. Databases are written at the "current" version
17 // number, but any previous version that can read the "compatible" one can make
18 // do with our database without *too* many bad effects.
19 const int kCurrentVersionNumber = 2;
20 const int kCompatibleVersionNumber = 1;
15 21
16 bool InitTables(sql::Connection& db) { 22 bool InitTables(sql::Connection& db) {
17 const char kTableSql[] = 23 const char kTableSql[] =
18 "CREATE TABLE IF NOT EXISTS images_by_url (" 24 "CREATE TABLE IF NOT EXISTS images_by_url ("
19 "page_url LONGVARCHAR NOT NULL," 25 "page_url LONGVARCHAR NOT NULL,"
20 "image_url LONGVARCHAR NOT NULL," 26 "image_url LONGVARCHAR NOT NULL,"
21 "image_data BLOB," 27 "image_data BLOB,"
22 "width INTEGER," 28 "width INTEGER,"
23 "height INTEGER" 29 "height INTEGER,"
30 "image_dominant_color INTEGER"
24 ")"; 31 ")";
25 if (!db.Execute(kTableSql)) 32 if (!db.Execute(kTableSql))
26 return false; 33 return false;
27 return true; 34 return true;
28 } 35 }
29 36
30 bool InitIndices(sql::Connection& db) { 37 bool InitIndices(sql::Connection& db) {
31 const char kIndexSql[] = 38 const char kIndexSql[] =
32 "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)"; 39 "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)";
33 if (!db.Execute(kIndexSql)) 40 if (!db.Execute(kIndexSql))
34 return false; 41 return false;
35 return true; 42 return true;
36 } 43 }
37 44
45 // V1 didn't store the dominant color of an image. Creates the column to store
46 // a dominant color in the database. The value will be filled when queried for a
47 // one time cost.
48 bool MigrateImagesWithNoDominantColor(sql::Connection& db) {
49 if (!db.DoesTableExist("images_by_url")) {
50 NOTREACHED() << "images_by_url table should exist before migration.";
51 return false;
52 }
53
54 if (!db.DoesColumnExist("images_by_url", "image_dominant_color")) {
55 // The initial version doesn't have the image_dominant_color column, it is
56 // added to the table here.
57 if (!db.Execute(
58 "ALTER TABLE images_by_url "
59 "ADD COLUMN image_dominant_color INTEGER")) {
60 return false;
61 }
62 }
63 return true;
64 }
65
66 sql::InitStatus EnsureCurrentVersion(sql::Connection& db,
67 sql::MetaTable& meta_table) {
68 // 1- Newer databases than designed for can't be read.
69 if (meta_table.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
70 LOG(WARNING) << "Image DB is too new.";
71 return sql::INIT_TOO_NEW;
72 }
73
74 int cur_version = meta_table.GetVersionNumber();
75
76 // 2- Put migration code here.
77
78 if (cur_version == 1) {
79 if (!MigrateImagesWithNoDominantColor(db)) {
80 LOG(WARNING) << "Unable to update image DB to version 1.";
81 return sql::INIT_FAILURE;
82 }
83 ++cur_version;
84 meta_table.SetVersionNumber(cur_version);
85 meta_table.SetCompatibleVersionNumber(
86 std::min(cur_version, kCompatibleVersionNumber));
87 }
88
89 // 3- When the version is too old, just try to continue anyway, there should
90 // not be a released product that makes a database too old to handle.
91 LOG_IF(WARNING, cur_version < kCurrentVersionNumber)
92 << "Image DB version " << cur_version << " is too old to handle.";
93
94 return sql::INIT_OK;
95 }
96
38 sql::InitStatus OpenDatabaseImpl(sql::Connection& db, 97 sql::InitStatus OpenDatabaseImpl(sql::Connection& db,
98 sql::MetaTable& meta_table,
39 const base::FilePath& db_path) { 99 const base::FilePath& db_path) {
40 DCHECK(!db.is_open()); 100 DCHECK(!db.is_open());
41 101
42 db.set_histogram_tag("BookmarkImages"); 102 db.set_histogram_tag("BookmarkImages");
43 // TODO(noyau): Set page and cache sizes? 103 // TODO(noyau): Set page and cache sizes?
44 // TODO(noyau): Set error callback? 104 // TODO(noyau): Set error callback?
45 105
46 // Run the database in exclusive mode. Nobody else should be accessing the 106 // Run the database in exclusive mode. Nobody else should be accessing the
47 // database while we're running, and this will give somewhat improved perf. 107 // database while running, and this will give somewhat improved performance.
48 db.set_exclusive_locking(); 108 db.set_exclusive_locking();
49 109
50 if (!db.Open(db_path)) 110 if (!db.Open(db_path))
51 return sql::INIT_FAILURE; 111 return sql::INIT_FAILURE;
52 112
53 // Scope initialization in a transaction so we can't be partially initialized. 113 // Scope initialization in a transaction so it can't be partially initialized.
54 sql::Transaction transaction(&db); 114 sql::Transaction transaction(&db);
55 if (!transaction.Begin()) 115 if (!transaction.Begin())
56 return sql::INIT_FAILURE; 116 return sql::INIT_FAILURE;
57 117
118 // Initialize the meta table.
119 int cur_version = meta_table.DoesTableExist(&db)
120 ? kCurrentVersionNumber
121 : 1; // Only v1 didn't have a meta table.
122 if (!meta_table.Init(&db, cur_version,
123 std::min(cur_version, kCompatibleVersionNumber)))
124 return sql::INIT_FAILURE;
125
58 // Create the tables. 126 // Create the tables.
59 if (!InitTables(db) || 127 if (!InitTables(db) || !InitIndices(db))
60 !InitIndices(db)) {
61 return sql::INIT_FAILURE; 128 return sql::INIT_FAILURE;
62 } 129
130 // Check the version.
131 sql::InitStatus version_status = EnsureCurrentVersion(db, meta_table);
132 if (version_status != sql::INIT_OK)
133 return version_status;
63 134
64 // Initialization is complete. 135 // Initialization is complete.
65 if (!transaction.Commit()) 136 if (!transaction.Commit())
66 return sql::INIT_FAILURE; 137 return sql::INIT_FAILURE;
67 138
68 return sql::INIT_OK; 139 return sql::INIT_OK;
69 } 140 }
70 141
71 } // namespace 142 } // namespace
72 143
(...skipping 10 matching lines...) Expand all
83 154
84 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 155 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
85 "SELECT COUNT(*) FROM images_by_url WHERE page_url = ?")); 156 "SELECT COUNT(*) FROM images_by_url WHERE page_url = ?"));
86 statement.BindString(0, page_url.possibly_invalid_spec()); 157 statement.BindString(0, page_url.possibly_invalid_spec());
87 158
88 int count = statement.Step() ? statement.ColumnInt(0) : 0; 159 int count = statement.Step() ? statement.ColumnInt(0) : 0;
89 160
90 return !!count; 161 return !!count;
91 } 162 }
92 163
93 void PersistentImageStore::Insert(const GURL& page_url, 164 void PersistentImageStore::Insert(
94 const GURL& image_url, 165 const GURL& page_url,
95 const gfx::Image& image) { 166 const enhanced_bookmarks::ImageRecord& record) {
96 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 167 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
97 if (OpenDatabase() != sql::INIT_OK) 168 if (OpenDatabase() != sql::INIT_OK)
98 return; 169 return;
99 170
100 Erase(page_url); // Remove previous image for this url, if any. 171 Erase(page_url); // Remove previous image for this url, if any.
101 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 172 sql::Statement statement(db_.GetCachedStatement(
173 SQL_FROM_HERE,
102 "INSERT INTO images_by_url " 174 "INSERT INTO images_by_url "
103 "(page_url, image_url, image_data, width, height)" 175 "(page_url, image_url, image_data, width, height, image_dominant_color)"
104 "VALUES (?, ?, ?, ?, ?)")); 176 "VALUES (?, ?, ?, ?, ?, ?)"));
105 177
106 statement.BindString(0, page_url.possibly_invalid_spec()); 178 statement.BindString(0, page_url.possibly_invalid_spec());
107 statement.BindString(1, image_url.possibly_invalid_spec()); 179 statement.BindString(1, record.url.possibly_invalid_spec());
108 180
109 scoped_refptr<base::RefCountedMemory> image_bytes = 181 scoped_refptr<base::RefCountedMemory> image_bytes =
110 enhanced_bookmarks::BytesForImage(image); 182 enhanced_bookmarks::BytesForImage(record.image);
111 183
112 // Insert an empty image in case encoding fails. 184 // Insert an empty image in case encoding fails.
113 if (!image_bytes.get()) 185 if (!image_bytes.get())
114 image_bytes = enhanced_bookmarks::BytesForImage(gfx::Image()); 186 image_bytes = enhanced_bookmarks::BytesForImage(gfx::Image());
115 187
116 CHECK(image_bytes.get()); 188 CHECK(image_bytes.get());
117 189
118 statement.BindBlob(2, image_bytes->front(), (int)image_bytes->size()); 190 statement.BindBlob(2, image_bytes->front(), (int)image_bytes->size());
119 191
120 statement.BindInt(3, image.Size().width()); 192 statement.BindInt(3, record.image.Size().width());
121 statement.BindInt(4, image.Size().height()); 193 statement.BindInt(4, record.image.Size().height());
194 statement.BindInt(5, record.dominant_color);
122 statement.Run(); 195 statement.Run();
123 } 196 }
124 197
125 void PersistentImageStore::Erase(const GURL& page_url) { 198 void PersistentImageStore::Erase(const GURL& page_url) {
126 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 199 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
127 if (OpenDatabase() != sql::INIT_OK) 200 if (OpenDatabase() != sql::INIT_OK)
128 return; 201 return;
129 202
130 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 203 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
131 "DELETE FROM images_by_url WHERE page_url = ?")); 204 "DELETE FROM images_by_url WHERE page_url = ?"));
132 statement.BindString(0, page_url.possibly_invalid_spec()); 205 statement.BindString(0, page_url.possibly_invalid_spec());
133 statement.Run(); 206 statement.Run();
134 } 207 }
135 208
136 std::pair<gfx::Image, GURL> PersistentImageStore::Get(const GURL& page_url) { 209 enhanced_bookmarks::ImageRecord PersistentImageStore::Get(
210 const GURL& page_url) {
137 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 211 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
212 enhanced_bookmarks::ImageRecord image_record;
138 if (OpenDatabase() != sql::INIT_OK) 213 if (OpenDatabase() != sql::INIT_OK)
139 return std::make_pair(gfx::Image(), GURL()); 214 return image_record;
140 215
141 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 216 bool stored_image_record_needs_update = false;
142 "SELECT image_data, image_url FROM images_by_url WHERE page_url = ?"));
143 217
144 statement.BindString(0, page_url.possibly_invalid_spec()); 218 // Scope the SELECT statement.
219 {
220 sql::Statement statement(db_.GetCachedStatement(
221 SQL_FROM_HERE,
222 "SELECT image_data, image_url, image_dominant_color FROM images_by_url "
223 "WHERE page_url = ?"));
145 224
146 while (statement.Step()) { 225 statement.BindString(0, page_url.possibly_invalid_spec());
226
227 if (!statement.Step())
228 return image_record;
229
230 // Image.
147 if (statement.ColumnByteLength(0) > 0) { 231 if (statement.ColumnByteLength(0) > 0) {
148 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); 232 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
149 statement.ColumnBlobAsVector(0, &data->data()); 233 statement.ColumnBlobAsVector(0, &data->data());
234 image_record.image = enhanced_bookmarks::ImageForBytes(data);
235 }
150 236
151 return std::make_pair(enhanced_bookmarks::ImageForBytes(data), 237 // URL.
152 GURL(statement.ColumnString(1))); 238 image_record.url = GURL(statement.ColumnString(1));
239
240 // Dominant color.
241 if (statement.ColumnType(2) != sql::COLUMN_TYPE_NULL) {
242 image_record.dominant_color = SkColor(statement.ColumnInt(2));
243 } else {
244 // The dominant color was not computed when the image was first
245 // stored.
246 // Compute it now.
247 image_record.dominant_color =
248 enhanced_bookmarks::DominantColorForImage(image_record.image);
249 stored_image_record_needs_update = true;
153 } 250 }
251
252 // Make sure there is only one record for page_url.
253 DCHECK(!statement.Step());
154 } 254 }
155 255
156 return std::make_pair(gfx::Image(), GURL()); 256 if (stored_image_record_needs_update) {
257 Erase(page_url);
258 Insert(page_url, image_record);
259 }
260
261 return image_record;
157 } 262 }
158 263
159 gfx::Size PersistentImageStore::GetSize(const GURL& page_url) { 264 gfx::Size PersistentImageStore::GetSize(const GURL& page_url) {
160 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 265 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
161 if (OpenDatabase() != sql::INIT_OK) 266 if (OpenDatabase() != sql::INIT_OK)
162 return gfx::Size(); 267 return gfx::Size();
163 268
164 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 269 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
165 "SELECT width, height FROM images_by_url WHERE page_url = ?")); 270 "SELECT width, height FROM images_by_url WHERE page_url = ?"));
166 271
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
210 sql::InitStatus PersistentImageStore::OpenDatabase() { 315 sql::InitStatus PersistentImageStore::OpenDatabase() {
211 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 316 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
212 317
213 if (db_.is_open()) 318 if (db_.is_open())
214 return sql::INIT_OK; 319 return sql::INIT_OK;
215 320
216 const size_t kAttempts = 2; 321 const size_t kAttempts = 2;
217 322
218 sql::InitStatus status = sql::INIT_FAILURE; 323 sql::InitStatus status = sql::INIT_FAILURE;
219 for (size_t i = 0; i < kAttempts; ++i) { 324 for (size_t i = 0; i < kAttempts; ++i) {
220 status = OpenDatabaseImpl(db_, path_); 325 status = OpenDatabaseImpl(db_, meta_table_, path_);
221 if (status == sql::INIT_OK) 326 if (status == sql::INIT_OK)
222 return status; 327 return status;
223 328
224 // Can't open, raze(). 329 // Can't open, raze().
225 if (db_.is_open()) 330 if (db_.is_open())
226 db_.Raze(); 331 db_.Raze();
227 db_.Close(); 332 db_.Close();
228 } 333 }
229 334
230 DCHECK(false) << "Can't open image DB"; 335 DCHECK(false) << "Can't open image DB";
231 return sql::INIT_FAILURE; 336 return sql::INIT_FAILURE;
232 } 337 }
OLDNEW
« no previous file with comments | « components/enhanced_bookmarks/persistent_image_store.h ('k') | components/enhanced_bookmarks/test_image_store.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698