OLD | NEW |
---|---|
(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 | |
OLD | NEW |