Allow immediate snapping after scrolling starts
[chromium-blink-merge.git] / components / enhanced_bookmarks / persistent_image_store.cc
blob0108c85fc6549013e878284c10b4f4925af4e04c
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
3 // found in the LICENSE file.
5 #include "components/enhanced_bookmarks/persistent_image_store.h"
7 #include "base/files/file.h"
8 #include "components/enhanced_bookmarks/image_store_util.h"
9 #include "sql/statement.h"
10 #include "sql/transaction.h"
11 #include "ui/gfx/geometry/size.h"
12 #include "url/gurl.h"
14 namespace {
16 bool InitTables(sql::Connection& db) {
17 const char kTableSql[] =
18 "CREATE TABLE IF NOT EXISTS images_by_url ("
19 "page_url LONGVARCHAR NOT NULL,"
20 "image_url LONGVARCHAR NOT NULL,"
21 "image_data BLOB,"
22 "width INTEGER,"
23 "height INTEGER"
24 ")";
25 if (!db.Execute(kTableSql))
26 return false;
27 return true;
30 bool InitIndices(sql::Connection& db) {
31 const char kIndexSql[] =
32 "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)";
33 if (!db.Execute(kIndexSql))
34 return false;
35 return true;
38 sql::InitStatus OpenDatabaseImpl(sql::Connection& db,
39 const base::FilePath& db_path) {
40 DCHECK(!db.is_open());
42 db.set_histogram_tag("BookmarkImages");
43 // TODO(noyau): Set page and cache sizes?
44 // TODO(noyau): Set error callback?
46 // 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.
48 db.set_exclusive_locking();
50 if (!db.Open(db_path))
51 return sql::INIT_FAILURE;
53 // Scope initialization in a transaction so we can't be partially initialized.
54 sql::Transaction transaction(&db);
55 if (!transaction.Begin())
56 return sql::INIT_FAILURE;
58 // Create the tables.
59 if (!InitTables(db) ||
60 !InitIndices(db)) {
61 return sql::INIT_FAILURE;
64 // Initialization is complete.
65 if (!transaction.Commit())
66 return sql::INIT_FAILURE;
68 return sql::INIT_OK;
71 } // namespace
73 PersistentImageStore::PersistentImageStore(const base::FilePath& path)
74 : ImageStore(),
75 path_(path.Append(
76 base::FilePath::FromUTF8Unsafe("BookmarkImageAndUrlStore.db"))) {
79 bool PersistentImageStore::HasKey(const GURL& page_url) {
80 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
81 if (OpenDatabase() != sql::INIT_OK)
82 return false;
84 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
85 "SELECT COUNT(*) FROM images_by_url WHERE page_url = ?"));
86 statement.BindString(0, page_url.possibly_invalid_spec());
88 int count = statement.Step() ? statement.ColumnInt(0) : 0;
90 return !!count;
93 void PersistentImageStore::Insert(const GURL& page_url,
94 const GURL& image_url,
95 const gfx::Image& image) {
96 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
97 if (OpenDatabase() != sql::INIT_OK)
98 return;
100 Erase(page_url); // Remove previous image for this url, if any.
101 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
102 "INSERT INTO images_by_url "
103 "(page_url, image_url, image_data, width, height)"
104 "VALUES (?, ?, ?, ?, ?)"));
106 statement.BindString(0, page_url.possibly_invalid_spec());
107 statement.BindString(1, image_url.possibly_invalid_spec());
109 scoped_refptr<base::RefCountedMemory> image_bytes =
110 enhanced_bookmarks::BytesForImage(image);
112 // Insert an empty image in case encoding fails.
113 if (!image_bytes.get())
114 image_bytes = enhanced_bookmarks::BytesForImage(gfx::Image());
116 CHECK(image_bytes.get());
118 statement.BindBlob(2, image_bytes->front(), (int)image_bytes->size());
120 statement.BindInt(3, image.Size().width());
121 statement.BindInt(4, image.Size().height());
122 statement.Run();
125 void PersistentImageStore::Erase(const GURL& page_url) {
126 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
127 if (OpenDatabase() != sql::INIT_OK)
128 return;
130 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
131 "DELETE FROM images_by_url WHERE page_url = ?"));
132 statement.BindString(0, page_url.possibly_invalid_spec());
133 statement.Run();
136 std::pair<gfx::Image, GURL> PersistentImageStore::Get(const GURL& page_url) {
137 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
138 if (OpenDatabase() != sql::INIT_OK)
139 return std::make_pair(gfx::Image(), GURL());
141 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
142 "SELECT image_data, image_url FROM images_by_url WHERE page_url = ?"));
144 statement.BindString(0, page_url.possibly_invalid_spec());
146 while (statement.Step()) {
147 if (statement.ColumnByteLength(0) > 0) {
148 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
149 statement.ColumnBlobAsVector(0, &data->data());
151 return std::make_pair(enhanced_bookmarks::ImageForBytes(data),
152 GURL(statement.ColumnString(1)));
156 return std::make_pair(gfx::Image(), GURL());
159 gfx::Size PersistentImageStore::GetSize(const GURL& page_url) {
160 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
161 if (OpenDatabase() != sql::INIT_OK)
162 return gfx::Size();
164 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
165 "SELECT width, height FROM images_by_url WHERE page_url = ?"));
167 statement.BindString(0, page_url.possibly_invalid_spec());
169 while (statement.Step()) {
170 if (statement.ColumnByteLength(0) > 0) {
171 int width = statement.ColumnInt(0);
172 int height = statement.ColumnInt(1);
173 return gfx::Size(width, height);
176 return gfx::Size();
179 void PersistentImageStore::GetAllPageUrls(std::set<GURL>* urls) {
180 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
181 DCHECK(urls->empty());
182 if (OpenDatabase() != sql::INIT_OK)
183 return;
185 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
186 "SELECT page_url FROM images_by_url"));
187 while (statement.Step())
188 urls->insert(GURL(statement.ColumnString(0)));
191 void PersistentImageStore::ClearAll() {
192 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
193 if (OpenDatabase() != sql::INIT_OK)
194 return;
196 sql::Statement statement(db_.GetCachedStatement(
197 SQL_FROM_HERE, "DELETE FROM images_by_url"));
198 statement.Run();
201 int64 PersistentImageStore::GetStoreSizeInBytes() {
202 base::File file(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
203 return file.IsValid() ? file.GetLength() : -1;
206 PersistentImageStore::~PersistentImageStore() {
207 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
210 sql::InitStatus PersistentImageStore::OpenDatabase() {
211 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
213 if (db_.is_open())
214 return sql::INIT_OK;
216 const size_t kAttempts = 2;
218 sql::InitStatus status = sql::INIT_FAILURE;
219 for (size_t i = 0; i < kAttempts; ++i) {
220 status = OpenDatabaseImpl(db_, path_);
221 if (status == sql::INIT_OK)
222 return status;
224 // Can't open, raze().
225 if (db_.is_open())
226 db_.Raze();
227 db_.Close();
230 DCHECK(false) << "Can't open image DB";
231 return sql::INIT_FAILURE;