Add 'did_proceed' and 'repeat_visit' to ClientMalwareReportRequest to track CTR.
[chromium-blink-merge.git] / components / offline_pages / offline_page_model.cc
blobf4d787050f1a2f70574b8710b053f20349f1e03c
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/metrics/histogram_macros.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/time/time.h"
16 #include "components/bookmarks/browser/bookmark_model.h"
17 #include "components/bookmarks/browser/bookmark_node.h"
18 #include "components/offline_pages/offline_page_item.h"
19 #include "components/offline_pages/offline_page_metadata_store.h"
20 #include "url/gurl.h"
22 using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult;
23 using SavePageResult = offline_pages::OfflinePageModel::SavePageResult;
25 namespace offline_pages {
27 namespace {
29 // Threshold for how old offline copy of a page should be before we offer to
30 // delete it to free up space.
31 const base::TimeDelta kPageCleanUpThreshold = base::TimeDelta::FromDays(30);
33 SavePageResult ToSavePageResult(ArchiverResult archiver_result) {
34 SavePageResult result;
35 switch (archiver_result) {
36 case ArchiverResult::SUCCESSFULLY_CREATED:
37 result = SavePageResult::SUCCESS;
38 break;
39 case ArchiverResult::ERROR_DEVICE_FULL:
40 result = SavePageResult::DEVICE_FULL;
41 break;
42 case ArchiverResult::ERROR_CONTENT_UNAVAILABLE:
43 result = SavePageResult::CONTENT_UNAVAILABLE;
44 break;
45 case ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED:
46 result = SavePageResult::ARCHIVE_CREATION_FAILED;
47 break;
48 case ArchiverResult::ERROR_CANCELED:
49 result = SavePageResult::CANCELLED;
50 break;
51 default:
52 NOTREACHED();
53 result = SavePageResult::CONTENT_UNAVAILABLE;
55 return result;
58 void DeleteArchiveFiles(const std::vector<base::FilePath>& paths_to_delete,
59 bool* success) {
60 DCHECK(success);
61 for (const auto& file_path : paths_to_delete) {
62 // Make sure delete happens on the left of || so that it is always executed.
63 *success = base::DeleteFile(file_path, false) || *success;
67 void EmptyDeleteCallback(OfflinePageModel::DeletePageResult /* result */) {
70 } // namespace
72 OfflinePageModel::OfflinePageModel(
73 scoped_ptr<OfflinePageMetadataStore> store,
74 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
75 : store_(store.Pass()),
76 is_loaded_(false),
77 task_runner_(task_runner),
78 scoped_observer_(this),
79 weak_ptr_factory_(this) {
80 store_->Load(base::Bind(&OfflinePageModel::OnLoadDone,
81 weak_ptr_factory_.GetWeakPtr()));
84 OfflinePageModel::~OfflinePageModel() {
87 void OfflinePageModel::Start(bookmarks::BookmarkModel* model) {
88 scoped_observer_.Add(model);
91 void OfflinePageModel::Shutdown() {
92 scoped_observer_.RemoveAll();
95 void OfflinePageModel::AddObserver(Observer* observer) {
96 observers_.AddObserver(observer);
99 void OfflinePageModel::RemoveObserver(Observer* observer) {
100 observers_.RemoveObserver(observer);
103 void OfflinePageModel::SavePage(const GURL& url,
104 int64 bookmark_id,
105 scoped_ptr<OfflinePageArchiver> archiver,
106 const SavePageCallback& callback) {
107 DCHECK(is_loaded_);
108 DCHECK(archiver.get());
109 archiver->CreateArchive(base::Bind(&OfflinePageModel::OnCreateArchiveDone,
110 weak_ptr_factory_.GetWeakPtr(), url,
111 bookmark_id, callback));
112 pending_archivers_.push_back(archiver.Pass());
115 void OfflinePageModel::DeletePageByBookmarkId(
116 int64 bookmark_id,
117 const DeletePageCallback& callback) {
118 DCHECK(is_loaded_);
119 std::vector<int64> bookmark_ids_to_delete;
120 bookmark_ids_to_delete.push_back(bookmark_id);
121 DeletePagesByBookmarkId(bookmark_ids_to_delete, callback);
124 void OfflinePageModel::DeletePagesByBookmarkId(
125 const std::vector<int64>& bookmark_ids,
126 const DeletePageCallback& callback) {
127 DCHECK(is_loaded_);
129 std::vector<base::FilePath> paths_to_delete;
130 for (const auto& bookmark_id : bookmark_ids) {
131 auto iter = offline_pages_.find(bookmark_id);
132 if (iter != offline_pages_.end()) {
133 paths_to_delete.push_back(iter->second.file_path);
137 if (paths_to_delete.empty()) {
138 InformDeletePageDone(callback, DeletePageResult::NOT_FOUND);
139 return;
142 bool* success = new bool(false);
143 task_runner_->PostTaskAndReply(
144 FROM_HERE,
145 base::Bind(&DeleteArchiveFiles, paths_to_delete, success),
146 base::Bind(&OfflinePageModel::OnDeleteArchiveFilesDone,
147 weak_ptr_factory_.GetWeakPtr(),
148 bookmark_ids,
149 callback,
150 base::Owned(success)));
153 const std::vector<OfflinePageItem> OfflinePageModel::GetAllPages() const {
154 DCHECK(is_loaded_);
155 std::vector<OfflinePageItem> offline_pages;
156 for (const auto& id_page_pair : offline_pages_)
157 offline_pages.push_back(id_page_pair.second);
158 return offline_pages;
161 const std::vector<OfflinePageItem> OfflinePageModel::GetPagesToCleanUp() const {
162 DCHECK(is_loaded_);
163 std::vector<OfflinePageItem> offline_pages;
164 base::Time now = base::Time::Now();
165 for (const auto& id_page_pair : offline_pages_) {
166 if (now - id_page_pair.second.creation_time > kPageCleanUpThreshold)
167 offline_pages.push_back(id_page_pair.second);
169 return offline_pages;
172 const OfflinePageItem* OfflinePageModel::GetPageByBookmarkId(
173 int64 bookmark_id) const {
174 const auto iter = offline_pages_.find(bookmark_id);
175 return iter != offline_pages_.end() ? &(iter->second) : nullptr;
178 const OfflinePageItem* OfflinePageModel::GetPageByOfflineURL(
179 const GURL& offline_url) const {
180 for (auto iter = offline_pages_.begin();
181 iter != offline_pages_.end();
182 ++iter) {
183 if (iter->second.GetOfflineURL() == offline_url)
184 return &(iter->second);
186 return nullptr;
189 OfflinePageMetadataStore* OfflinePageModel::GetStoreForTesting() {
190 return store_.get();
193 void OfflinePageModel::OnCreateArchiveDone(const GURL& requested_url,
194 int64 bookmark_id,
195 const SavePageCallback& callback,
196 OfflinePageArchiver* archiver,
197 ArchiverResult archiver_result,
198 const GURL& url,
199 const base::FilePath& file_path,
200 int64 file_size) {
201 if (requested_url != url) {
202 DVLOG(1) << "Saved URL does not match requested URL.";
203 // TODO(fgorski): We have created an archive for a wrong URL. It should be
204 // deleted from here, once archiver has the right functionality.
205 InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED);
206 DeletePendingArchiver(archiver);
207 return;
210 if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
211 SavePageResult result = ToSavePageResult(archiver_result);
212 InformSavePageDone(callback, result);
213 DeletePendingArchiver(archiver);
214 return;
217 OfflinePageItem offline_page_item(url, bookmark_id, file_path, file_size,
218 base::Time::Now());
219 store_->AddOfflinePage(
220 offline_page_item,
221 base::Bind(&OfflinePageModel::OnAddOfflinePageDone,
222 weak_ptr_factory_.GetWeakPtr(), archiver, callback,
223 offline_page_item));
226 void OfflinePageModel::OnAddOfflinePageDone(OfflinePageArchiver* archiver,
227 const SavePageCallback& callback,
228 const OfflinePageItem& offline_page,
229 bool success) {
230 SavePageResult result;
231 if (success) {
232 offline_pages_[offline_page.bookmark_id] = offline_page;
233 result = SavePageResult::SUCCESS;
234 UMA_HISTOGRAM_MEMORY_KB(
235 "OfflinePages.PageSize", offline_page.file_size / 1024);
236 } else {
237 result = SavePageResult::STORE_FAILURE;
239 InformSavePageDone(callback, result);
240 DeletePendingArchiver(archiver);
243 void OfflinePageModel::BookmarkModelChanged() {
246 void OfflinePageModel::BookmarkNodeRemoved(
247 bookmarks::BookmarkModel* model,
248 const bookmarks::BookmarkNode* parent,
249 int old_index,
250 const bookmarks::BookmarkNode* node,
251 const std::set<GURL>& removed_urls) {
252 if (!is_loaded_) {
253 delayed_tasks_.push_back(
254 base::Bind(&OfflinePageModel::DeletePageByBookmarkId,
255 weak_ptr_factory_.GetWeakPtr(),
256 node->id(),
257 base::Bind(&EmptyDeleteCallback)));
258 return;
260 DeletePageByBookmarkId(node->id(), base::Bind(&EmptyDeleteCallback));
263 void OfflinePageModel::OnLoadDone(
264 bool success,
265 const std::vector<OfflinePageItem>& offline_pages) {
266 DCHECK(!is_loaded_);
267 is_loaded_ = true;
269 if (success) {
270 for (const auto& offline_page : offline_pages)
271 offline_pages_[offline_page.bookmark_id] = offline_page;
274 // Run all the delayed tasks.
275 for (const auto& delayed_task : delayed_tasks_)
276 delayed_task.Run();
277 delayed_tasks_.clear();
279 FOR_EACH_OBSERVER(Observer, observers_, OfflinePageModelLoaded(this));
282 void OfflinePageModel::InformSavePageDone(const SavePageCallback& callback,
283 SavePageResult result) {
284 UMA_HISTOGRAM_ENUMERATION(
285 "OfflinePages.SavePageResult",
286 static_cast<int>(result),
287 static_cast<int>(SavePageResult::RESULT_COUNT));
288 callback.Run(result);
291 void OfflinePageModel::DeletePendingArchiver(OfflinePageArchiver* archiver) {
292 pending_archivers_.erase(std::find(
293 pending_archivers_.begin(), pending_archivers_.end(), archiver));
296 void OfflinePageModel::OnDeleteArchiveFilesDone(
297 const std::vector<int64>& bookmark_ids,
298 const DeletePageCallback& callback,
299 const bool* success) {
300 DCHECK(success);
302 if (!*success) {
303 InformDeletePageDone(callback, DeletePageResult::DEVICE_FAILURE);
304 return;
307 store_->RemoveOfflinePages(
308 bookmark_ids,
309 base::Bind(&OfflinePageModel::OnRemoveOfflinePagesDone,
310 weak_ptr_factory_.GetWeakPtr(), bookmark_ids, callback));
313 void OfflinePageModel::OnRemoveOfflinePagesDone(
314 const std::vector<int64>& bookmark_ids,
315 const DeletePageCallback& callback,
316 bool success) {
317 // Delete the offline page from the in memory cache regardless of success in
318 // store.
319 base::Time now = base::Time::Now();
320 for (int64 bookmark_id : bookmark_ids) {
321 auto iter = offline_pages_.find(bookmark_id);
322 if (iter == offline_pages_.end())
323 continue;
324 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.PageLifetime",
325 (now - iter->second.creation_time).InMinutes(),
327 base::TimeDelta::FromDays(365).InMinutes(),
328 100);
329 offline_pages_.erase(iter);
331 // Deleting multiple pages always succeeds when it gets to this point.
332 InformDeletePageDone(
333 callback,
334 (success || bookmark_ids.size() > 1) ? DeletePageResult::SUCCESS
335 : DeletePageResult::STORE_FAILURE);
338 void OfflinePageModel::InformDeletePageDone(const DeletePageCallback& callback,
339 DeletePageResult result) {
340 UMA_HISTOGRAM_ENUMERATION(
341 "OfflinePages.DeletePageResult",
342 static_cast<int>(result),
343 static_cast<int>(DeletePageResult::RESULT_COUNT));
344 callback.Run(result);
347 } // namespace offline_pages