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

Side by Side 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, 6 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
(Empty)
1 // Copyright 2015 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
5 #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.
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/location.h"
10 #include "base/sequenced_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "components/offline_pages/offline_page_item.h"
13 #include "components/offline_pages/proto/offline_pages.pb.h"
14 #include "third_party/leveldatabase/env_chromium.h"
15 #include "third_party/leveldatabase/src/include/leveldb/db.h"
16 #include "url/gurl.h"
17
18 namespace offline_pages {
19
20 namespace {
21
22 const char kOffinePageKeyStart[] = "offline-page1-";
23 const char kOffinePageKeyEnd[] = "offline-page2-";
24
25 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
26 // outlive the slice.
27 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
28 leveldb::Slice MakeSlice(const base::StringPiece& s) {
29 return leveldb::Slice(s.begin(), s.size());
30 }
31
32 std::string MakeKey(const std::string& key) {
33 return kOffinePageKeyStart + key;
34 }
35
36 std::string SerializeOfflinePage(const OfflinePageItem& item) {
37 offline_pages_proto::OfflinePageItem item_proto;
38 item_proto.set_url(item.url.spec());
39 item_proto.set_title(item.title);
40 item_proto.set_version(item.version);
41 item_proto.set_file_path(item.file_path.value());
42 item_proto.set_file_size(item.file_size);
43 item_proto.set_creation_time(item.creation_time.ToInternalValue());
44 item_proto.set_last_access_time(item.last_access_time.ToInternalValue());
45 return item_proto.SerializeAsString();
46 }
47
48 bool DeserializeOfflinePage(const std::string& serialized,
49 OfflinePageItem* item) {
50 offline_pages_proto::OfflinePageItem item_proto;
51 if (!item_proto.ParseFromString(serialized)) {
52 LOG(ERROR) << "Failed to parse serialized offline page metadata.";
53 return false;
54 }
55 if (!item) {
56 LOG(ERROR) << "Item pointer should be initialized.";
57 return false;
58 }
59
60 item->url = GURL(item_proto.url());
61 item->title = item_proto.title();
62 item->version = item_proto.version();
63 item->file_path = base::FilePath(item_proto.file_path());
64 if (item_proto.has_file_size()) {
65 item->file_size = item_proto.file_size();
66 }
67 if (item_proto.has_creation_time()) {
68 item->creation_time =
69 base::Time::FromInternalValue(item_proto.creation_time());
70 }
71 if (item_proto.has_last_access_time()) {
72 item->last_access_time =
73 base::Time::FromInternalValue(item_proto.last_access_time());
74 }
75 return true;
76 }
77
78 } // namespace
79
80 class OfflinePageMetadataStoreImpl::Backend
81 : public base::RefCountedThreadSafe<OfflinePageMetadataStoreImpl::Backend> {
82 public:
83 Backend(const base::FilePath& path,
84 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner);
85
86 void Close();
87
88 // Blocking implementations methods.
89 void Load(const LoadCallback& callback);
90 void AddOfflinePage(const OfflinePageItem& offline_page_item,
91 const UpdateCallback& callback);
92 void RemoveOfflinePage(const GURL& page_url, const UpdateCallback& callback);
93 void Destroy(const UpdateCallback& callback);
94
95 private:
96 friend class base::RefCountedThreadSafe<Backend>;
97 ~Backend();
98
99 Status EnsureStoreIsOpen();
100
101 bool LoadOfflinePages(std::vector<std::string>* serialized_offline_pages);
102
103 const base::FilePath path_;
104 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
105 scoped_ptr<leveldb::DB> db_;
106 };
107
108 OfflinePageMetadataStoreImpl::Backend::Backend(
109 const base::FilePath& path,
110 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner)
111 : path_(path), foreground_task_runner_(foreground_task_runner) {
112 }
113
114 OfflinePageMetadataStoreImpl::Backend::~Backend() {
115 }
116
117 void OfflinePageMetadataStoreImpl::Backend::Load(const LoadCallback& callback) {
118 if (EnsureStoreIsOpen() == FAILED_TO_OPEN_STORE) {
119 foreground_task_runner_->PostTask(
120 FROM_HERE, base::Bind(callback, FAILED_TO_OPEN_STORE,
121 std::vector<OfflinePageItem>()));
122 return;
123 }
124
125 std::vector<std::string> serialized_offline_pages;
126 if (!LoadOfflinePages(&serialized_offline_pages)) {
127 foreground_task_runner_->PostTask(
128 FROM_HERE, base::Bind(callback, FAILED_TO_LOAD_STORE,
129 std::vector<OfflinePageItem>()));
130 return;
131 }
132
133 std::vector<OfflinePageItem> result;
134 for (auto iter = serialized_offline_pages.cbegin();
135 iter != serialized_offline_pages.cend(); ++iter) {
136 OfflinePageItem item;
137 if (!DeserializeOfflinePage(*iter, &item)) {
138 foreground_task_runner_->PostTask(
139 FROM_HERE, base::Bind(callback, FAILED_TO_DESERIALIZE,
140 std::vector<OfflinePageItem>()));
141 return;
142 }
143 result.push_back(item);
144 }
145
146 foreground_task_runner_->PostTask(FROM_HERE,
147 base::Bind(callback, SUCCESS, result));
148 }
149
150 OfflinePageMetadataStore::Status
151 OfflinePageMetadataStoreImpl::Backend::EnsureStoreIsOpen() {
152 if (!db_.get()) {
153 leveldb::Options options;
154 options.create_if_missing = true;
155 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
156 leveldb::DB* db;
157 leveldb::Status status =
158 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
159 if (!status.ok()) {
160 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
161 << status.ToString();
162 return FAILED_TO_OPEN_STORE;
163 }
164 db_.reset(db);
165 }
166
167 return SUCCESS;
168 }
169
170 void OfflinePageMetadataStoreImpl::Backend::Close() {
171 DVLOG(1) << "Closing the offline page metadata store.";
172 db_.reset();
173 }
174
175 void OfflinePageMetadataStoreImpl::Backend::AddOfflinePage(
176 const OfflinePageItem& offline_page_item,
177 const UpdateCallback& callback) {
178 DVLOG(1) << "Saving an entry in offline pages store with URL: "
179 << offline_page_item.url.spec();
180 Status status = EnsureStoreIsOpen();
181 if (status == FAILED_TO_OPEN_STORE) {
182 LOG(ERROR) << "OfflinePage store is not open.";
183 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
184 return;
185 }
186
187 leveldb::WriteOptions write_options;
188 write_options.sync = true;
189
190 std::string key = MakeKey(offline_page_item.url.spec());
191 leveldb::Slice key_slice = MakeSlice(key);
192
193 std::string serialized_value = SerializeOfflinePage(offline_page_item);
194 leveldb::Slice value_slice = MakeSlice(serialized_value);
195
196 leveldb::Status s = db_->Put(write_options, key_slice, value_slice);
197 if (!s.ok()) {
198 LOG(ERROR) << "Offline page store level DB Put failed for: "
199 << offline_page_item.url.spec() << " Status: " << s.ToString();
200 }
201
202 status = s.ok() ? SUCCESS : FAILED_TO_UPDATE_STORE;
203 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
204 }
205
206 void OfflinePageMetadataStoreImpl::Backend::RemoveOfflinePage(
207 const GURL& page_url,
208 const UpdateCallback& callback) {
209 DVLOG(1) << "Deleting an entry in offline pages store with URL: "
210 << page_url.spec();
211 Status status = EnsureStoreIsOpen();
212 if (status == FAILED_TO_OPEN_STORE) {
213 LOG(ERROR) << "OfflinePage store is not open.";
214 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
215 return;
216 }
217
218 leveldb::WriteOptions write_options;
219 write_options.sync = true;
220
221 std::string key = MakeKey(page_url.spec());
222 leveldb::Slice key_slice = MakeSlice(key);
223 leveldb::Status s = db_->Delete(write_options, key_slice);
224 if (!s.ok()) {
225 LOG(ERROR) << "Offline page store LevelDB Delete failed for: "
226 << page_url.spec() << " Status: " << s.ToString();
227 }
228
229 status = s.ok() ? SUCCESS : FAILED_TO_UPDATE_STORE;
230 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
231 }
232
233 bool OfflinePageMetadataStoreImpl::Backend::LoadOfflinePages(
234 std::vector<std::string>* serialized_offline_pages) {
235 leveldb::ReadOptions read_options;
236 read_options.verify_checksums = true;
237
238 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
239 leveldb::Slice from_key = MakeSlice(kOffinePageKeyStart);
240 for (iter->Seek(from_key);
241 iter->Valid() && iter->key().ToString() < kOffinePageKeyEnd;
242 iter->Next()) {
243 if (iter->value().empty()) {
244 LOG(ERROR) << "Error reading offline page metadata with key "
245 << iter->key().ToString();
246 return false;
247 }
248 DVLOG(1) << "Found incoming message with URL " << iter->key().ToString();
249 serialized_offline_pages->push_back(iter->value().ToString());
250 }
251
252 return true;
253 }
254
255 void OfflinePageMetadataStoreImpl::Backend::Destroy(
256 const UpdateCallback& callback) {
257 DVLOG(1) << "Destroying Offline Page Metadata store.";
258 db_.reset();
259 const leveldb::Status s =
260 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
261 if (!s.ok())
262 LOG(ERROR) << "Destroy failed: " << s.ToString();
263
264 Status status = s.ok() ? SUCCESS : FAILED_TO_DESTROY_STORE;
265 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, status));
266 }
267
268 OfflinePageMetadataStoreImpl::OfflinePageMetadataStoreImpl(
269 const base::FilePath& path,
270 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
271 : backend_(new Backend(path, base::ThreadTaskRunnerHandle::Get())),
272 blocking_task_runner_(blocking_task_runner) {
273 }
274
275 OfflinePageMetadataStoreImpl::~OfflinePageMetadataStoreImpl() {
276 }
277
278 void OfflinePageMetadataStoreImpl::AddOfflinePage(
279 const OfflinePageItem& offline_page_item,
280 const UpdateCallback& callback) {
281 blocking_task_runner_->PostTask(
282 FROM_HERE,
283 base::Bind(&OfflinePageMetadataStoreImpl::Backend::AddOfflinePage,
284 backend_, offline_page_item, callback));
285 }
286
287 void OfflinePageMetadataStoreImpl::RemoveOfflinePage(
288 const GURL& page_url,
289 const UpdateCallback& callback) {
290 blocking_task_runner_->PostTask(
291 FROM_HERE,
292 base::Bind(&OfflinePageMetadataStoreImpl::Backend::RemoveOfflinePage,
293 backend_, page_url, callback));
294 }
295
296 void OfflinePageMetadataStoreImpl::Load(const LoadCallback& callback) {
297 blocking_task_runner_->PostTask(
298 FROM_HERE, base::Bind(&OfflinePageMetadataStoreImpl::Backend::Load,
299 backend_, callback));
300 }
301
302 void OfflinePageMetadataStoreImpl::Destroy(const UpdateCallback& callback) {
303 blocking_task_runner_->PostTask(
304 FROM_HERE, base::Bind(&OfflinePageMetadataStoreImpl::Backend::Destroy,
305 backend_, callback));
306 }
307
308 void OfflinePageMetadataStoreImpl::Close() {
309 blocking_task_runner_->PostTask(
310 FROM_HERE,
311 base::Bind(&OfflinePageMetadataStoreImpl::Backend::Close, backend_));
312 }
313
314 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698