OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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 #include "chrome/browser/bookmarks/enhanced/image_store.h" | |
sky
2014/04/30 18:09:13
nit: newline between 3/4.
Kibeom Kim (inactive)
2014/05/01 19:02:26
Done.
| |
5 | |
6 #include "base/logging.h" | |
7 #include "base/memory/ref_counted_memory.h" | |
8 #include "sql/statement.h" | |
9 #include "sql/transaction.h" | |
10 | |
11 #if defined(OS_IOS) | |
12 #import "chrome/browser/bookmarks/enhanced/image_store_ios.h" | |
13 #endif // defined(OS_IOS) | |
14 | |
15 namespace { | |
16 // The two methods below archive and unarchive an image to and from a bag of | |
17 // bytes. There is no API on gfx::Image capable of doing it while preserving the | |
18 // scale of the image. On iOS instead of the 1x representation (that may lose | |
19 // resolution) the archive contains a iOS specific archive of the UIImage | |
20 // representation. | |
21 scoped_refptr<base::RefCountedMemory> bytesForImage(gfx::Image image) { | |
sky
2014/04/30 18:09:13
BytesForImage
Kibeom Kim (inactive)
2014/05/01 19:02:26
Done.
| |
22 #if defined(OS_IOS) | |
23 // Forces the image to have a UIImage representation. | |
24 image.ToUIImage(); | |
25 return image_store_ios::bytesForImage(image); | |
26 #else | |
27 return image.As1xPNGBytes(); | |
sky
2014/04/30 18:09:13
Why are you only 1x image?
Kibeom Kim (inactive)
2014/05/01 19:02:26
I think above comment #16-#20 explains this issue.
| |
28 #endif // defined(OS_IOS) | |
29 } // namespace | |
30 | |
31 gfx::Image imageForBytes(scoped_refptr<base::RefCountedMemory> bytes) { | |
sky
2014/04/30 18:09:13
ImageForBytes
Kibeom Kim (inactive)
2014/05/01 19:02:26
Done.
| |
32 #if defined(OS_IOS) | |
33 return image_store_ios::imageForBytes(bytes); | |
34 #else | |
35 return gfx::Image::CreateFrom1xPNGBytes(bytes->front(), bytes->size()); | |
36 #endif // defined(OS_IOS) | |
37 } | |
38 } // namespace | |
39 | |
40 void ImageStore::ChangeImageURL(const GURL& from, const GURL& to) { | |
sky
2014/04/30 18:09:13
Make order match header.
Kibeom Kim (inactive)
2014/05/01 19:02:26
Done.
| |
41 if (!HasKey(from)) | |
42 return; | |
43 GURL image_url; | |
44 gfx::Image image(Get(from, image_url)); | |
45 | |
46 Erase(from); | |
47 Insert(to, image_url, image); | |
48 } | |
49 | |
50 // Removes all images. | |
51 void ImageStore::ClearAll() { | |
52 std::vector<GURL> all_keys = AllKeys(); | |
53 for (std::vector<GURL>::const_iterator it = all_keys.begin(); | |
54 it != all_keys.end(); | |
55 ++it) { | |
56 Erase(*it); | |
57 } | |
58 } | |
59 | |
60 MemoryImageStore::MemoryImageStore() : ImageStore() {} | |
61 MemoryImageStore::~MemoryImageStore() {} | |
62 | |
63 bool MemoryImageStore::HasKey(const GURL& page_url) { | |
64 return store_.find(page_url) != store_.end(); | |
65 } | |
66 | |
67 void MemoryImageStore::Insert(const GURL& page_url, | |
68 const GURL& image_url, | |
69 const gfx::Image& image) { | |
70 Erase(page_url); | |
71 store_.insert(std::make_pair( | |
72 page_url, | |
73 std::make_pair(image, | |
74 image_url))); | |
75 } | |
76 | |
77 void MemoryImageStore::Erase(const GURL& page_url) { | |
78 store_.erase(page_url); | |
79 } | |
80 | |
81 gfx::Image MemoryImageStore::Get(const GURL& page_url, GURL& image_url) { | |
82 ImageMap::const_iterator pair_enumerator = store_.find(page_url); | |
83 if (pair_enumerator == store_.end()) | |
84 return gfx::Image(); | |
85 | |
86 image_url = store_[page_url].second; | |
87 return store_[page_url].first; | |
88 } | |
89 | |
90 gfx::Size MemoryImageStore::GetSize(const GURL& page_url) { | |
91 ImageMap::const_iterator pair_enumerator = store_.find(page_url); | |
92 if (pair_enumerator == store_.end()) | |
93 return gfx::Size(); | |
94 | |
95 return store_[page_url].first.Size(); | |
96 } | |
97 | |
98 std::vector<GURL> MemoryImageStore::AllKeys() { | |
99 std::vector<GURL> result; | |
100 | |
101 for (ImageMap::const_iterator it = store_.begin(); it != store_.end(); ++it) { | |
102 result.push_back(it->first); | |
103 } | |
104 return result; | |
105 } | |
106 | |
107 namespace { | |
108 bool InitTables(sql::Connection& db) { | |
109 const char kTableSql[] = | |
110 "CREATE TABLE IF NOT EXISTS images_by_url (" | |
111 " page_url LONGVARCHAR NOT NULL," | |
112 " image_url LONGVARCHAR NOT NULL," | |
113 " image_data BLOB," | |
114 " width INTEGER," | |
115 " height INTEGER" | |
116 ")"; | |
117 if (!db.Execute(kTableSql)) | |
118 return false; | |
119 return true; | |
120 } | |
121 | |
122 bool InitIndices(sql::Connection& db) { | |
123 const char kIndexSql[] = | |
124 "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)"; | |
125 if (!db.Execute(kIndexSql)) | |
126 return false; | |
127 return true; | |
128 } | |
129 | |
130 sql::InitStatus OpenDatabaseImpl(sql::Connection& db, | |
131 const base::FilePath& db_path) { | |
132 DCHECK(!db.is_open()); | |
133 | |
134 db.set_histogram_tag("BookmarkImages"); | |
135 // TODO(noyau): Set page and cache sizes? | |
136 // TODO(noyau): Set error callback? | |
137 | |
138 // Run the database in exclusive mode. Nobody else should be accessing the | |
139 // database while we're running, and this will give somewhat improved perf. | |
140 db.set_exclusive_locking(); | |
141 | |
142 if (!db.Open(db_path)) | |
143 return sql::INIT_FAILURE; | |
144 | |
145 // Scope initialization in a transaction so we can't be partially initialized. | |
146 sql::Transaction transaction(&db); | |
147 if (!transaction.Begin()) | |
148 return sql::INIT_FAILURE; | |
149 | |
150 // Create the tables. | |
151 if (!InitTables(db) || | |
152 !InitIndices(db)) { | |
153 return sql::INIT_FAILURE; | |
154 } | |
155 | |
156 // Initialization is complete. | |
157 if (!transaction.Commit()) | |
158 return sql::INIT_FAILURE; | |
159 | |
160 return sql::INIT_OK; | |
161 } | |
162 | |
163 } // namespace | |
164 | |
165 PersistentImageStore::PersistentImageStore(const base::FilePath& path) | |
166 : ImageStore(), path_(path.Append("BookmarkImageAndUrlStore.db")) {} | |
167 | |
168 PersistentImageStore::~PersistentImageStore() {} | |
169 | |
170 bool PersistentImageStore::HasKey(const GURL& page_url) { | |
171 OpenDatabase(); | |
172 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
173 "SELECT COUNT(*) FROM images_by_url WHERE page_url = ?")); | |
174 statement.BindString(0, page_url.possibly_invalid_spec()); | |
175 | |
176 int count = statement.Step() ? statement.ColumnInt(0) : 0; | |
177 | |
178 return !!count; | |
179 } | |
180 | |
181 void PersistentImageStore::Insert(const GURL& page_url, | |
182 const GURL& image_url, | |
183 const gfx::Image& image) { | |
184 Erase(page_url); // Remove previous image for this url, if any. | |
185 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
186 "INSERT INTO images_by_url " | |
187 " (page_url, image_url, image_data, width, height)" | |
188 " VALUES (?, ?, ?, ?, ?)")); | |
189 | |
190 statement.BindString(0, page_url.possibly_invalid_spec()); | |
191 statement.BindString(1, image_url.possibly_invalid_spec()); | |
192 | |
193 scoped_refptr<base::RefCountedMemory> data = bytesForImage(image); | |
194 statement.BindBlob(2, data->front(), data->size()); | |
195 | |
196 statement.BindInt64(3, image.Size().width()); | |
197 statement.BindInt64(4, image.Size().height()); | |
198 statement.Run(); | |
199 } | |
200 | |
201 void PersistentImageStore::Erase(const GURL& page_url) { | |
202 OpenDatabase(); | |
203 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
204 "DELETE FROM images_by_url WHERE page_url = ?")); | |
205 statement.BindString(0, page_url.possibly_invalid_spec()); | |
206 statement.Run(); | |
207 } | |
208 | |
209 gfx::Image PersistentImageStore::Get(const GURL& page_url, GURL& image_url) { | |
210 OpenDatabase(); | |
211 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
212 "SELECT image_data, image_url FROM images_by_url WHERE page_url = ?")); | |
213 | |
214 statement.BindString(0, page_url.possibly_invalid_spec()); | |
215 | |
216 while (statement.Step()) { | |
217 if (statement.ColumnByteLength(0) > 0) { | |
218 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); | |
219 statement.ColumnBlobAsVector(0, &data->data()); | |
220 image_url = GURL(statement.ColumnString(1)); | |
221 return imageForBytes(data); | |
222 } | |
223 } | |
224 return gfx::Image(); | |
225 } | |
226 | |
227 gfx::Size PersistentImageStore::GetSize(const GURL& page_url) { | |
228 OpenDatabase(); | |
229 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
230 "SELECT width, height FROM images_by_url WHERE page_url = ?")); | |
231 | |
232 statement.BindString(0, page_url.possibly_invalid_spec()); | |
233 | |
234 while (statement.Step()) { | |
235 if (statement.ColumnByteLength(0) > 0) { | |
236 int64 width = statement.ColumnInt64(0); | |
237 int64 height = statement.ColumnInt64(1); | |
238 return gfx::Size(width, height); | |
239 } | |
240 } | |
241 return gfx::Size(); | |
242 } | |
243 | |
244 std::vector<GURL> PersistentImageStore::AllKeys() { | |
245 OpenDatabase(); | |
246 std::vector<GURL> result; | |
247 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
248 "SELECT page_url FROM images_by_url")); | |
249 while (statement.Step()) | |
250 result.push_back(GURL(statement.ColumnString(0))); | |
251 | |
252 return result; | |
253 } | |
254 | |
255 sql::InitStatus PersistentImageStore::OpenDatabase() { | |
256 if (db_.is_open()) | |
257 return sql::INIT_OK; | |
258 | |
259 const size_t kAttempts = 2; | |
260 | |
261 sql::InitStatus status = sql::INIT_FAILURE; | |
262 for (size_t i = 0; i < kAttempts; ++i) { | |
263 status = OpenDatabaseImpl(db_, path_); | |
264 if (status == sql::INIT_OK) | |
265 return status; | |
266 | |
267 // Can't open, raze(). | |
268 if (db_.is_open()) | |
269 db_.Raze(); | |
270 db_.Close(); | |
271 } | |
272 CHECK(false) << "Can't open image DB"; | |
273 return sql::INIT_FAILURE; | |
274 } | |
OLD | NEW |