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

Unified Diff: components/offline_pages/offline_page_metadata_store_impl.cc

Issue 1160283003: [Offline] Creates metadata store interface for offline pages (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@offline-pages-1
Patch Set: Adding BUILD.gn updates Created 5 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: components/offline_pages/offline_page_metadata_store_impl.cc
diff --git a/components/offline_pages/offline_page_metadata_store_impl.cc b/components/offline_pages/offline_page_metadata_store_impl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5d5aafb7b24ab884323eb13cb3aaadc026f75ffb
--- /dev/null
+++ b/components/offline_pages/offline_page_metadata_store_impl.cc
@@ -0,0 +1,314 @@
+// Copyright 2015 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/offline_pages/offline_page_metadata_store_impl.h"
Dmitry Titov 2015/06/04 00:25:53 If we move the dependency on leveldb to embedder,
fgorski 2015/06/05 21:28:42 Done.
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "components/offline_pages/offline_page_item.h"
+#include "components/offline_pages/proto/offline_pages.pb.h"
+#include "third_party/leveldatabase/env_chromium.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "url/gurl.h"
+
+namespace offline_pages {
+
+namespace {
+
+const char kOffinePageKeyStart[] = "offline-page1-";
+const char kOffinePageKeyEnd[] = "offline-page2-";
+
+// Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
+// outlive the slice.
+// For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
+leveldb::Slice MakeSlice(const base::StringPiece& s) {
+ return leveldb::Slice(s.begin(), s.size());
+}
+
+std::string MakeKey(const std::string& key) {
+ return kOffinePageKeyStart + key;
+}
+
+std::string SerializeOfflinePage(const OfflinePageItem& item) {
+ offline_pages_proto::OfflinePageItem item_proto;
+ item_proto.set_url(item.url.spec());
+ item_proto.set_title(item.title);
+ item_proto.set_version(item.version);
+ item_proto.set_file_path(item.file_path.value());
+ item_proto.set_file_size(item.file_size);
+ item_proto.set_creation_time(item.creation_time.ToInternalValue());
+ item_proto.set_last_access_time(item.last_access_time.ToInternalValue());
+ return item_proto.SerializeAsString();
+}
+
+bool DeserializeOfflinePage(const std::string& serialized,
+ OfflinePageItem* item) {
+ offline_pages_proto::OfflinePageItem item_proto;
+ if (!item_proto.ParseFromString(serialized)) {
+ LOG(ERROR) << "Failed to parse serialized offline page metadata.";
+ return false;
+ }
+ if (!item) {
+ LOG(ERROR) << "Item pointer should be initialized.";
+ return false;
+ }
+
+ item->url = GURL(item_proto.url());
+ item->title = item_proto.title();
+ item->version = item_proto.version();
+ item->file_path = base::FilePath(item_proto.file_path());
+ if (item_proto.has_file_size()) {
+ item->file_size = item_proto.file_size();
+ }
+ if (item_proto.has_creation_time()) {
+ item->creation_time =
+ base::Time::FromInternalValue(item_proto.creation_time());
+ }
+ if (item_proto.has_last_access_time()) {
+ item->last_access_time =
+ base::Time::FromInternalValue(item_proto.last_access_time());
+ }
+ return true;
+}
+
+} // namespace
+
+class OfflinePageMetadataStoreImpl::Backend
+ : public base::RefCountedThreadSafe<OfflinePageMetadataStoreImpl::Backend> {
+ public:
+ Backend(const base::FilePath& path,
+ scoped_refptr<base::SequencedTaskRunner> foreground_task_runner);
+
+ void Close();
+
+ // Blocking implementations methods.
+ void Load(const LoadCallback& callback);
+ void AddOfflinePage(const OfflinePageItem& offline_page_item,
+ const UpdateCallback& callback);
+ void RemoveOfflinePage(const GURL& page_url, const UpdateCallback& callback);
+ void Destroy(const UpdateCallback& callback);
+
+ private:
+ friend class base::RefCountedThreadSafe<Backend>;
+ ~Backend();
+
+ Status EnsureStoreIsOpen();
+
+ bool LoadOfflinePages(std::vector<std::string>* serialized_offline_pages);
+
+ const base::FilePath path_;
+ scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
+ scoped_ptr<leveldb::DB> db_;
+};
+
+OfflinePageMetadataStoreImpl::Backend::Backend(
+ const base::FilePath& path,
+ scoped_refptr<base::SequencedTaskRunner> foreground_task_runner)
+ : path_(path), foreground_task_runner_(foreground_task_runner) {
+}
+
+OfflinePageMetadataStoreImpl::Backend::~Backend() {
+}
+
+void OfflinePageMetadataStoreImpl::Backend::Load(const LoadCallback& callback) {
+ if (EnsureStoreIsOpen() == FAILED_TO_OPEN_STORE) {
+ foreground_task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, FAILED_TO_OPEN_STORE,
+ std::vector<OfflinePageItem>()));
+ return;
+ }
+
+ std::vector<std::string> serialized_offline_pages;
+ if (!LoadOfflinePages(&serialized_offline_pages)) {
+ foreground_task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, FAILED_TO_LOAD_STORE,
+ std::vector<OfflinePageItem>()));
+ return;
+ }
+
+ std::vector<OfflinePageItem> result;
+ for (auto iter = serialized_offline_pages.cbegin();
+ iter != serialized_offline_pages.cend(); ++iter) {
+ OfflinePageItem item;
+ if (!DeserializeOfflinePage(*iter, &item)) {
+ foreground_task_runner_->PostTask(
+ FROM_HERE, base::Bind(callback, FAILED_TO_DESERIALIZE,
+ std::vector<OfflinePageItem>()));
+ return;
+ }
+ result.push_back(item);
+ }
+
+ foreground_task_runner_->PostTask(FROM_HERE,
+ base::Bind(callback, SUCCESS, result));
+}
+
+OfflinePageMetadataStore::Status
+OfflinePageMetadataStoreImpl::Backend::EnsureStoreIsOpen() {
+ if (!db_.get()) {
+ leveldb::Options options;
+ options.create_if_missing = true;
+ options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
+ leveldb::DB* db;
+ leveldb::Status status =
+ leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
+ if (!status.ok()) {
+ LOG(ERROR) << "Failed to open database " << path_.value() << ": "
+ << status.ToString();
+ return FAILED_TO_OPEN_STORE;
+ }
+ db_.reset(db);
+ }
+
+ return SUCCESS;
+}
+
+void OfflinePageMetadataStoreImpl::Backend::Close() {
+ DVLOG(1) << "Closing the offline page metadata store.";
+ db_.reset();
+}
+
+void OfflinePageMetadataStoreImpl::Backend::AddOfflinePage(
+ const OfflinePageItem& offline_page_item,
+ const UpdateCallback& callback) {
+ DVLOG(1) << "Saving an entry in offline pages store with URL: "
+ << offline_page_item.url.spec();
+ Status status = EnsureStoreIsOpen();
+ if (status == FAILED_TO_OPEN_STORE) {
+ LOG(ERROR) << "OfflinePage store is not open.";
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
+ return;
+ }
+
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+
+ std::string key = MakeKey(offline_page_item.url.spec());
+ leveldb::Slice key_slice = MakeSlice(key);
+
+ std::string serialized_value = SerializeOfflinePage(offline_page_item);
+ leveldb::Slice value_slice = MakeSlice(serialized_value);
+
+ leveldb::Status s = db_->Put(write_options, key_slice, value_slice);
+ if (!s.ok()) {
+ LOG(ERROR) << "Offline page store level DB Put failed for: "
+ << offline_page_item.url.spec() << " Status: " << s.ToString();
+ }
+
+ status = s.ok() ? SUCCESS : FAILED_TO_UPDATE_STORE;
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
+}
+
+void OfflinePageMetadataStoreImpl::Backend::RemoveOfflinePage(
+ const GURL& page_url,
+ const UpdateCallback& callback) {
+ DVLOG(1) << "Deleting an entry in offline pages store with URL: "
+ << page_url.spec();
+ Status status = EnsureStoreIsOpen();
+ if (status == FAILED_TO_OPEN_STORE) {
+ LOG(ERROR) << "OfflinePage store is not open.";
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
+ return;
+ }
+
+ leveldb::WriteOptions write_options;
+ write_options.sync = true;
+
+ std::string key = MakeKey(page_url.spec());
+ leveldb::Slice key_slice = MakeSlice(key);
+ leveldb::Status s = db_->Delete(write_options, key_slice);
+ if (!s.ok()) {
+ LOG(ERROR) << "Offline page store LevelDB Delete failed for: "
+ << page_url.spec() << " Status: " << s.ToString();
+ }
+
+ status = s.ok() ? SUCCESS : FAILED_TO_UPDATE_STORE;
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
+}
+
+bool OfflinePageMetadataStoreImpl::Backend::LoadOfflinePages(
+ std::vector<std::string>* serialized_offline_pages) {
+ leveldb::ReadOptions read_options;
+ read_options.verify_checksums = true;
+
+ scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
+ leveldb::Slice from_key = MakeSlice(kOffinePageKeyStart);
+ for (iter->Seek(from_key);
+ iter->Valid() && iter->key().ToString() < kOffinePageKeyEnd;
+ iter->Next()) {
+ if (iter->value().empty()) {
+ LOG(ERROR) << "Error reading offline page metadata with key "
+ << iter->key().ToString();
+ return false;
+ }
+ DVLOG(1) << "Found incoming message with URL " << iter->key().ToString();
+ serialized_offline_pages->push_back(iter->value().ToString());
+ }
+
+ return true;
+}
+
+void OfflinePageMetadataStoreImpl::Backend::Destroy(
+ const UpdateCallback& callback) {
+ DVLOG(1) << "Destroying Offline Page Metadata store.";
+ db_.reset();
+ const leveldb::Status s =
+ leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
+ if (!s.ok())
+ LOG(ERROR) << "Destroy failed: " << s.ToString();
+
+ Status status = s.ok() ? SUCCESS : FAILED_TO_DESTROY_STORE;
+ foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
+}
+
+OfflinePageMetadataStoreImpl::OfflinePageMetadataStoreImpl(
+ const base::FilePath& path,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
+ : backend_(new Backend(path, base::ThreadTaskRunnerHandle::Get())),
+ blocking_task_runner_(blocking_task_runner) {
+}
+
+OfflinePageMetadataStoreImpl::~OfflinePageMetadataStoreImpl() {
+}
+
+void OfflinePageMetadataStoreImpl::AddOfflinePage(
+ const OfflinePageItem& offline_page_item,
+ const UpdateCallback& callback) {
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&OfflinePageMetadataStoreImpl::Backend::AddOfflinePage,
+ backend_, offline_page_item, callback));
+}
+
+void OfflinePageMetadataStoreImpl::RemoveOfflinePage(
+ const GURL& page_url,
+ const UpdateCallback& callback) {
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&OfflinePageMetadataStoreImpl::Backend::RemoveOfflinePage,
+ backend_, page_url, callback));
+}
+
+void OfflinePageMetadataStoreImpl::Load(const LoadCallback& callback) {
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&OfflinePageMetadataStoreImpl::Backend::Load,
+ backend_, callback));
+}
+
+void OfflinePageMetadataStoreImpl::Destroy(const UpdateCallback& callback) {
+ blocking_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&OfflinePageMetadataStoreImpl::Backend::Destroy,
+ backend_, callback));
+}
+
+void OfflinePageMetadataStoreImpl::Close() {
+ blocking_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&OfflinePageMetadataStoreImpl::Backend::Close, backend_));
+}
+
+} // namespace offline_pages

Powered by Google App Engine
This is Rietveld 408576698