Index: components/enhanced_bookmarks/persistent_image_store.cc |
diff --git a/components/enhanced_bookmarks/persistent_image_store.cc b/components/enhanced_bookmarks/persistent_image_store.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..49c062abab8e1a1c07176e646b0c03d2ad882b84 |
--- /dev/null |
+++ b/components/enhanced_bookmarks/persistent_image_store.cc |
@@ -0,0 +1,226 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/enhanced_bookmarks/persistent_image_store.h" |
+ |
+#include "components/enhanced_bookmarks/image_store_util.h" |
+#include "sql/statement.h" |
+#include "sql/transaction.h" |
+#include "ui/gfx/geometry/size.h" |
+#include "url/gurl.h" |
+ |
+namespace { |
+ |
+bool InitTables(sql::Connection& db) { |
+ const char kTableSql[] = |
+ "CREATE TABLE IF NOT EXISTS images_by_url (" |
+ "page_url LONGVARCHAR NOT NULL," |
+ "image_url LONGVARCHAR NOT NULL," |
+ "image_data BLOB," |
+ "width INTEGER," |
+ "height INTEGER" |
+ ")"; |
+ if (!db.Execute(kTableSql)) |
+ return false; |
+ return true; |
+} |
+ |
+bool InitIndices(sql::Connection& db) { |
+ const char kIndexSql[] = |
+ "CREATE INDEX IF NOT EXISTS images_by_url_idx ON images_by_url(page_url)"; |
+ if (!db.Execute(kIndexSql)) |
+ return false; |
+ return true; |
+} |
+ |
+sql::InitStatus OpenDatabaseImpl(sql::Connection& db, |
+ const base::FilePath& db_path) { |
+ DCHECK(!db.is_open()); |
+ |
+ db.set_histogram_tag("BookmarkImages"); |
+ // TODO(noyau): Set page and cache sizes? |
+ // TODO(noyau): Set error callback? |
+ |
+ // Run the database in exclusive mode. Nobody else should be accessing the |
+ // database while we're running, and this will give somewhat improved perf. |
+ db.set_exclusive_locking(); |
+ |
+ if (!db.Open(db_path)) |
+ return sql::INIT_FAILURE; |
+ |
+ // Scope initialization in a transaction so we can't be partially initialized. |
+ sql::Transaction transaction(&db); |
+ if (!transaction.Begin()) |
+ return sql::INIT_FAILURE; |
+ |
+ // Create the tables. |
+ if (!InitTables(db) || |
+ !InitIndices(db)) { |
+ return sql::INIT_FAILURE; |
+ } |
+ |
+ // Initialization is complete. |
+ if (!transaction.Commit()) |
+ return sql::INIT_FAILURE; |
+ |
+ return sql::INIT_OK; |
+} |
+ |
+} // namespace |
+ |
+PersistentImageStore::PersistentImageStore(const base::FilePath& path) |
+ : ImageStore(), |
+ path_(path.Append( |
+ base::FilePath::FromUTF8Unsafe("BookmarkImageAndUrlStore.db"))) { |
+} |
+ |
+bool PersistentImageStore::HasKey(const GURL& page_url) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (OpenDatabase() != sql::INIT_OK) |
+ return false; |
+ |
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
+ "SELECT COUNT(*) FROM images_by_url WHERE page_url = ?")); |
+ statement.BindString(0, page_url.possibly_invalid_spec()); |
+ |
+ int count = statement.Step() ? statement.ColumnInt(0) : 0; |
+ |
+ return !!count; |
+} |
+ |
+void PersistentImageStore::Insert(const GURL& page_url, |
+ const GURL& image_url, |
+ const gfx::Image& image) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (OpenDatabase() != sql::INIT_OK) |
+ return; |
+ |
+ Erase(page_url); // Remove previous image for this url, if any. |
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
+ "INSERT INTO images_by_url " |
+ "(page_url, image_url, image_data, width, height)" |
+ "VALUES (?, ?, ?, ?, ?)")); |
+ |
+ statement.BindString(0, page_url.possibly_invalid_spec()); |
+ statement.BindString(1, image_url.possibly_invalid_spec()); |
+ |
+ scoped_refptr<base::RefCountedMemory> image_bytes = |
+ enhanced_bookmarks::BytesForImage(image); |
+ |
+ // Insert an empty image in case encoding fails. |
+ if (!image_bytes.get()) |
+ image_bytes = enhanced_bookmarks::BytesForImage(gfx::Image()); |
+ |
+ CHECK(image_bytes.get()); |
+ |
+ statement.BindBlob(2, image_bytes->front(), (int)image_bytes->size()); |
+ |
+ statement.BindInt(3, image.Size().width()); |
+ statement.BindInt(4, image.Size().height()); |
+ statement.Run(); |
+} |
+ |
+void PersistentImageStore::Erase(const GURL& page_url) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (OpenDatabase() != sql::INIT_OK) |
+ return; |
+ |
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
+ "DELETE FROM images_by_url WHERE page_url = ?")); |
+ statement.BindString(0, page_url.possibly_invalid_spec()); |
+ statement.Run(); |
+} |
+ |
+std::pair<gfx::Image, GURL> PersistentImageStore::Get(const GURL& page_url) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (OpenDatabase() != sql::INIT_OK) |
+ return std::make_pair(gfx::Image(), GURL()); |
+ |
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
+ "SELECT image_data, image_url FROM images_by_url WHERE page_url = ?")); |
+ |
+ statement.BindString(0, page_url.possibly_invalid_spec()); |
+ |
+ while (statement.Step()) { |
+ if (statement.ColumnByteLength(0) > 0) { |
+ scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); |
+ statement.ColumnBlobAsVector(0, &data->data()); |
+ |
+ return std::make_pair(enhanced_bookmarks::ImageForBytes(data), |
+ GURL(statement.ColumnString(1))); |
+ } |
+ } |
+ |
+ return std::make_pair(gfx::Image(), GURL()); |
+} |
+ |
+gfx::Size PersistentImageStore::GetSize(const GURL& page_url) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (OpenDatabase() != sql::INIT_OK) |
+ return gfx::Size(); |
+ |
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
+ "SELECT width, height FROM images_by_url WHERE page_url = ?")); |
+ |
+ statement.BindString(0, page_url.possibly_invalid_spec()); |
+ |
+ while (statement.Step()) { |
+ if (statement.ColumnByteLength(0) > 0) { |
+ int width = statement.ColumnInt(0); |
+ int height = statement.ColumnInt(1); |
+ return gfx::Size(width, height); |
+ } |
+ } |
+ return gfx::Size(); |
+} |
+ |
+void PersistentImageStore::GetAllPageUrls(std::set<GURL>* urls) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(urls->empty()); |
+ if (OpenDatabase() != sql::INIT_OK) |
+ return; |
+ |
+ sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
+ "SELECT page_url FROM images_by_url")); |
+ while (statement.Step()) |
+ urls->insert(GURL(statement.ColumnString(0))); |
+} |
+ |
+void PersistentImageStore::ClearAll() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (OpenDatabase() != sql::INIT_OK) |
+ return; |
+ |
+ sql::Statement statement(db_.GetCachedStatement( |
+ SQL_FROM_HERE, "DELETE FROM images_by_url")); |
+ statement.Run(); |
+} |
+ |
+PersistentImageStore::~PersistentImageStore() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+} |
+ |
+sql::InitStatus PersistentImageStore::OpenDatabase() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ if (db_.is_open()) |
+ return sql::INIT_OK; |
+ |
+ const size_t kAttempts = 2; |
+ |
+ sql::InitStatus status = sql::INIT_FAILURE; |
+ for (size_t i = 0; i < kAttempts; ++i) { |
+ status = OpenDatabaseImpl(db_, path_); |
+ if (status == sql::INIT_OK) |
+ return status; |
+ |
+ // Can't open, raze(). |
+ if (db_.is_open()) |
+ db_.Raze(); |
+ db_.Close(); |
+ } |
+ |
+ DCHECK(false) << "Can't open image DB"; |
+ return sql::INIT_FAILURE; |
+} |