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"
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"
20 using ArchiverResult
= offline_pages::OfflinePageArchiver::ArchiverResult
;
21 using SavePageResult
= offline_pages::OfflinePageModel::SavePageResult
;
23 namespace offline_pages
{
27 SavePageResult
ToSavePageResult(ArchiverResult archiver_result
) {
28 SavePageResult result
;
29 switch (archiver_result
) {
30 case ArchiverResult::SUCCESSFULLY_CREATED
:
31 result
= SavePageResult::SUCCESS
;
33 case ArchiverResult::ERROR_DEVICE_FULL
:
34 result
= SavePageResult::DEVICE_FULL
;
36 case ArchiverResult::ERROR_CONTENT_UNAVAILABLE
:
37 result
= SavePageResult::CONTENT_UNAVAILABLE
;
39 case ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED
:
40 result
= SavePageResult::ARCHIVE_CREATION_FAILED
;
42 case ArchiverResult::ERROR_CANCELED
:
43 result
= SavePageResult::CANCELLED
;
47 result
= SavePageResult::CONTENT_UNAVAILABLE
;
52 void DeleteArchiveFiles(const std::vector
<base::FilePath
>& paths_to_delete
,
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 */) {
66 OfflinePageModel::OfflinePageModel(
67 scoped_ptr
<OfflinePageMetadataStore
> store
,
68 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
)
69 : store_(store
.Pass()),
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
,
99 scoped_ptr
<OfflinePageArchiver
> archiver
,
100 const SavePageCallback
& callback
) {
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(
111 const DeletePageCallback
& callback
) {
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
) {
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
);
136 bool* success
= new bool(false);
137 task_runner_
->PostTaskAndReply(
139 base::Bind(&DeleteArchiveFiles
, paths_to_delete
, success
),
140 base::Bind(&OfflinePageModel::OnDeleteArchiveFilesDone
,
141 weak_ptr_factory_
.GetWeakPtr(),
144 base::Owned(success
)));
147 const std::vector
<OfflinePageItem
> OfflinePageModel::GetAllPages() const {
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(
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
;
169 const OfflinePageItem
* OfflinePageModel::GetPageByOfflineURL(
170 const GURL
& offline_url
) const {
171 for (auto iter
= offline_pages_
.begin();
172 iter
!= offline_pages_
.end();
174 if (iter
->second
.GetOfflineURL() == offline_url
)
175 return &(iter
->second
);
180 OfflinePageMetadataStore
* OfflinePageModel::GetStoreForTesting() {
184 void OfflinePageModel::OnCreateArchiveDone(const GURL
& requested_url
,
186 const SavePageCallback
& callback
,
187 OfflinePageArchiver
* archiver
,
188 ArchiverResult archiver_result
,
190 const base::FilePath
& file_path
,
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
);
201 if (archiver_result
!= ArchiverResult::SUCCESSFULLY_CREATED
) {
202 SavePageResult result
= ToSavePageResult(archiver_result
);
203 InformSavePageDone(callback
, result
);
204 DeletePendingArchiver(archiver
);
208 OfflinePageItem
offline_page_item(url
, bookmark_id
, file_path
, file_size
,
210 store_
->AddOfflinePage(
212 base::Bind(&OfflinePageModel::OnAddOfflinePageDone
,
213 weak_ptr_factory_
.GetWeakPtr(), archiver
, callback
,
217 void OfflinePageModel::OnAddOfflinePageDone(OfflinePageArchiver
* archiver
,
218 const SavePageCallback
& callback
,
219 const OfflinePageItem
& offline_page
,
221 SavePageResult result
;
223 offline_pages_
[offline_page
.bookmark_id
] = offline_page
;
224 result
= SavePageResult::SUCCESS
;
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
,
239 const bookmarks::BookmarkNode
* node
,
240 const std::set
<GURL
>& removed_urls
) {
242 delayed_tasks_
.push_back(
243 base::Bind(&OfflinePageModel::DeletePageByBookmarkId
,
244 weak_ptr_factory_
.GetWeakPtr(),
246 base::Bind(&EmptyDeleteCallback
)));
249 DeletePageByBookmarkId(node
->id(), base::Bind(&EmptyDeleteCallback
));
252 void OfflinePageModel::OnLoadDone(
254 const std::vector
<OfflinePageItem
>& offline_pages
) {
258 // TODO(fgorski): Report the UMA upon failure. Cache should probably start
259 // empty. See if we can do something about it.
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_
)
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
) {
290 callback
.Run(DeletePageResult::DEVICE_FAILURE
);
294 store_
->RemoveOfflinePages(
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
,
304 // Delete the offline page from the in memory cache regardless of success in
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
);
312 callback
.Run(DeletePageResult::STORE_FAILURE
);
315 } // namespace offline_pages