[Offline pages] Delete offline page when a bookmark is deleted
[chromium-blink-merge.git] / components / offline_pages / offline_page_model.cc
blobf658ff59edb666b582ee6ad2a98fe3606771d199
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.
5 #include "components/offline_pages/offline_page_model.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/files/file_util.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/sequenced_task_runner.h"
14 #include "components/bookmarks/browser/bookmark_model.h"
15 #include "components/bookmarks/browser/bookmark_node.h"
16 #include "components/offline_pages/offline_page_item.h"
17 #include "components/offline_pages/offline_page_metadata_store.h"
18 #include "url/gurl.h"
20 using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult;
21 using SavePageResult = offline_pages::OfflinePageModel::SavePageResult;
23 namespace offline_pages {
25 namespace {
27 SavePageResult ToSavePageResult(ArchiverResult archiver_result) {
28 SavePageResult result;
29 switch (archiver_result) {
30 case ArchiverResult::SUCCESSFULLY_CREATED:
31 result = SavePageResult::SUCCESS;
32 break;
33 case ArchiverResult::ERROR_DEVICE_FULL:
34 result = SavePageResult::DEVICE_FULL;
35 break;
36 case ArchiverResult::ERROR_CONTENT_UNAVAILABLE:
37 result = SavePageResult::CONTENT_UNAVAILABLE;
38 break;
39 case ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED:
40 result = SavePageResult::ARCHIVE_CREATION_FAILED;
41 break;
42 case ArchiverResult::ERROR_CANCELED:
43 result = SavePageResult::CANCELLED;
44 break;
45 default:
46 NOTREACHED();
47 result = SavePageResult::CONTENT_UNAVAILABLE;
49 return result;
52 void DeleteArchiveFiles(const std::vector<base::FilePath>& paths_to_delete,
53 bool* success) {
54 DCHECK(success);
55 for (const auto& file_path : paths_to_delete) {
56 // Make sure delete happens on the left of || so that it is always executed.
57 *success = base::DeleteFile(file_path, false) || *success;
61 void EmptyDeleteCallback(OfflinePageModel::DeletePageResult /* result */) {
64 } // namespace
66 OfflinePageModel::OfflinePageModel(
67 scoped_ptr<OfflinePageMetadataStore> store,
68 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
69 : store_(store.Pass()),
70 is_loaded_(false),
71 task_runner_(task_runner),
72 scoped_observer_(this),
73 weak_ptr_factory_(this) {
74 store_->Load(base::Bind(&OfflinePageModel::OnLoadDone,
75 weak_ptr_factory_.GetWeakPtr()));
78 OfflinePageModel::~OfflinePageModel() {
81 void OfflinePageModel::Start(bookmarks::BookmarkModel* model) {
82 scoped_observer_.Add(model);
85 void OfflinePageModel::Shutdown() {
86 scoped_observer_.RemoveAll();
89 void OfflinePageModel::AddObserver(Observer* observer) {
90 observers_.AddObserver(observer);
93 void OfflinePageModel::RemoveObserver(Observer* observer) {
94 observers_.RemoveObserver(observer);
97 void OfflinePageModel::SavePage(const GURL& url,
98 int64 bookmark_id,
99 scoped_ptr<OfflinePageArchiver> archiver,
100 const SavePageCallback& callback) {
101 DCHECK(is_loaded_);
102 DCHECK(archiver.get());
103 archiver->CreateArchive(base::Bind(&OfflinePageModel::OnCreateArchiveDone,
104 weak_ptr_factory_.GetWeakPtr(), url,
105 bookmark_id, callback));
106 pending_archivers_.push_back(archiver.Pass());
109 void OfflinePageModel::DeletePageByBookmarkId(
110 int64 bookmark_id,
111 const DeletePageCallback& callback) {
112 DCHECK(is_loaded_);
113 std::vector<int64> bookmark_ids_to_delete;
114 bookmark_ids_to_delete.push_back(bookmark_id);
115 DeletePagesByBookmarkId(bookmark_ids_to_delete, callback);
118 void OfflinePageModel::DeletePagesByBookmarkId(
119 const std::vector<int64>& bookmark_ids,
120 const DeletePageCallback& callback) {
121 DCHECK(is_loaded_);
123 std::vector<base::FilePath> paths_to_delete;
124 for (const auto& bookmark_id : bookmark_ids) {
125 auto iter = offline_pages_.find(bookmark_id);
126 if (iter != offline_pages_.end()) {
127 paths_to_delete.push_back(iter->second.file_path);
131 if (paths_to_delete.empty()) {
132 callback.Run(DeletePageResult::NOT_FOUND);
133 return;
136 bool* success = new bool(false);
137 task_runner_->PostTaskAndReply(
138 FROM_HERE,
139 base::Bind(&DeleteArchiveFiles, paths_to_delete, success),
140 base::Bind(&OfflinePageModel::OnDeleteArchiveFilesDone,
141 weak_ptr_factory_.GetWeakPtr(),
142 bookmark_ids,
143 callback,
144 base::Owned(success)));
147 const std::vector<OfflinePageItem> OfflinePageModel::GetAllPages() const {
148 DCHECK(is_loaded_);
149 std::vector<OfflinePageItem> offline_pages;
150 for (const auto& id_page_pair : offline_pages_)
151 offline_pages.push_back(id_page_pair.second);
152 return offline_pages;
155 bool OfflinePageModel::GetPageByBookmarkId(
156 int64 bookmark_id,
157 OfflinePageItem* offline_page) const {
158 DCHECK(offline_page);
160 const auto iter = offline_pages_.find(bookmark_id);
161 if (iter != offline_pages_.end()) {
162 *offline_page = iter->second;
163 return true;
166 return false;
169 const OfflinePageItem* OfflinePageModel::GetPageByOfflineURL(
170 const GURL& offline_url) const {
171 for (auto iter = offline_pages_.begin();
172 iter != offline_pages_.end();
173 ++iter) {
174 if (iter->second.GetOfflineURL() == offline_url)
175 return &(iter->second);
177 return nullptr;
180 OfflinePageMetadataStore* OfflinePageModel::GetStoreForTesting() {
181 return store_.get();
184 void OfflinePageModel::OnCreateArchiveDone(const GURL& requested_url,
185 int64 bookmark_id,
186 const SavePageCallback& callback,
187 OfflinePageArchiver* archiver,
188 ArchiverResult archiver_result,
189 const GURL& url,
190 const base::FilePath& file_path,
191 int64 file_size) {
192 if (requested_url != url) {
193 DVLOG(1) << "Saved URL does not match requested URL.";
194 // TODO(fgorski): We have created an archive for a wrong URL. It should be
195 // deleted from here, once archiver has the right functionality.
196 InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED);
197 DeletePendingArchiver(archiver);
198 return;
201 if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
202 SavePageResult result = ToSavePageResult(archiver_result);
203 InformSavePageDone(callback, result);
204 DeletePendingArchiver(archiver);
205 return;
208 OfflinePageItem offline_page_item(url, bookmark_id, file_path, file_size,
209 base::Time::Now());
210 store_->AddOfflinePage(
211 offline_page_item,
212 base::Bind(&OfflinePageModel::OnAddOfflinePageDone,
213 weak_ptr_factory_.GetWeakPtr(), archiver, callback,
214 offline_page_item));
217 void OfflinePageModel::OnAddOfflinePageDone(OfflinePageArchiver* archiver,
218 const SavePageCallback& callback,
219 const OfflinePageItem& offline_page,
220 bool success) {
221 SavePageResult result;
222 if (success) {
223 offline_pages_[offline_page.bookmark_id] = offline_page;
224 result = SavePageResult::SUCCESS;
225 } else {
226 result = SavePageResult::STORE_FAILURE;
228 InformSavePageDone(callback, result);
229 DeletePendingArchiver(archiver);
232 void OfflinePageModel::BookmarkModelChanged() {
235 void OfflinePageModel::BookmarkNodeRemoved(
236 bookmarks::BookmarkModel* model,
237 const bookmarks::BookmarkNode* parent,
238 int old_index,
239 const bookmarks::BookmarkNode* node,
240 const std::set<GURL>& removed_urls) {
241 if (!is_loaded_) {
242 delayed_tasks_.push_back(
243 base::Bind(&OfflinePageModel::DeletePageByBookmarkId,
244 weak_ptr_factory_.GetWeakPtr(),
245 node->id(),
246 base::Bind(&EmptyDeleteCallback)));
247 return;
249 DeletePageByBookmarkId(node->id(), base::Bind(&EmptyDeleteCallback));
252 void OfflinePageModel::OnLoadDone(
253 bool success,
254 const std::vector<OfflinePageItem>& offline_pages) {
255 DCHECK(!is_loaded_);
256 is_loaded_ = true;
258 // TODO(fgorski): Report the UMA upon failure. Cache should probably start
259 // empty. See if we can do something about it.
260 if (success) {
261 for (const auto& offline_page : offline_pages)
262 offline_pages_[offline_page.bookmark_id] = offline_page;
265 // Run all the delayed tasks.
266 for (const auto& delayed_task : delayed_tasks_)
267 delayed_task.Run();
268 delayed_tasks_.clear();
270 FOR_EACH_OBSERVER(Observer, observers_, OfflinePageModelLoaded(this));
273 void OfflinePageModel::InformSavePageDone(const SavePageCallback& callback,
274 SavePageResult result) {
275 callback.Run(result);
278 void OfflinePageModel::DeletePendingArchiver(OfflinePageArchiver* archiver) {
279 pending_archivers_.erase(std::find(
280 pending_archivers_.begin(), pending_archivers_.end(), archiver));
283 void OfflinePageModel::OnDeleteArchiveFilesDone(
284 const std::vector<int64>& bookmark_ids,
285 const DeletePageCallback& callback,
286 const bool* success) {
287 DCHECK(success);
289 if (!*success) {
290 callback.Run(DeletePageResult::DEVICE_FAILURE);
291 return;
294 store_->RemoveOfflinePages(
295 bookmark_ids,
296 base::Bind(&OfflinePageModel::OnRemoveOfflinePagesDone,
297 weak_ptr_factory_.GetWeakPtr(), bookmark_ids, callback));
300 void OfflinePageModel::OnRemoveOfflinePagesDone(
301 const std::vector<int64>& bookmark_ids,
302 const DeletePageCallback& callback,
303 bool success) {
304 // Delete the offline page from the in memory cache regardless of success in
305 // store.
306 for (int64 bookmark_id : bookmark_ids)
307 offline_pages_.erase(bookmark_id);
308 // Deleting multiple pages always succeeds when it gets to this point.
309 if (success || bookmark_ids.size() > 1)
310 callback.Run(DeletePageResult::SUCCESS);
311 else
312 callback.Run(DeletePageResult::STORE_FAILURE);
315 } // namespace offline_pages