OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/offline_pages/offline_page_model.h" | 5 #include "components/offline_pages/offline_page_model.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
11 #include "base/location.h" | 11 #include "base/location.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
14 #include "base/sequenced_task_runner.h" | 14 #include "base/sequenced_task_runner.h" |
| 15 #include "base/thread_task_runner_handle.h" |
15 #include "base/time/time.h" | 16 #include "base/time/time.h" |
16 #include "components/bookmarks/browser/bookmark_model.h" | 17 #include "components/bookmarks/browser/bookmark_model.h" |
17 #include "components/bookmarks/browser/bookmark_node.h" | 18 #include "components/bookmarks/browser/bookmark_node.h" |
18 #include "components/offline_pages/offline_page_item.h" | 19 #include "components/offline_pages/offline_page_item.h" |
19 #include "components/offline_pages/offline_page_metadata_store.h" | 20 #include "components/offline_pages/offline_page_metadata_store.h" |
20 #include "url/gurl.h" | 21 #include "url/gurl.h" |
21 | 22 |
22 using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult; | 23 using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult; |
23 using SavePageResult = offline_pages::OfflinePageModel::SavePageResult; | 24 using SavePageResult = offline_pages::OfflinePageModel::SavePageResult; |
24 | 25 |
25 namespace offline_pages { | 26 namespace offline_pages { |
26 | 27 |
27 namespace { | 28 namespace { |
28 | 29 |
29 // Threshold for how old offline copy of a page should be before we offer to | 30 // Threshold for how old offline copy of a page should be before we offer to |
30 // delete it to free up space. | 31 // delete it to free up space. |
31 const base::TimeDelta kPageCleanUpThreshold = base::TimeDelta::FromDays(30); | 32 const base::TimeDelta kPageCleanUpThreshold = base::TimeDelta::FromDays(30); |
32 | 33 |
| 34 // The delay for the final deletion to kick in after the page is marked for |
| 35 // deletion. The value set here is a bit longer that the duration of the |
| 36 // snackbar that offers undo. |
| 37 const base::TimeDelta kFinalDeletionDelay = base::TimeDelta::FromSeconds(6); |
| 38 |
33 SavePageResult ToSavePageResult(ArchiverResult archiver_result) { | 39 SavePageResult ToSavePageResult(ArchiverResult archiver_result) { |
34 SavePageResult result; | 40 SavePageResult result; |
35 switch (archiver_result) { | 41 switch (archiver_result) { |
36 case ArchiverResult::SUCCESSFULLY_CREATED: | 42 case ArchiverResult::SUCCESSFULLY_CREATED: |
37 result = SavePageResult::SUCCESS; | 43 result = SavePageResult::SUCCESS; |
38 break; | 44 break; |
39 case ArchiverResult::ERROR_DEVICE_FULL: | 45 case ArchiverResult::ERROR_DEVICE_FULL: |
40 result = SavePageResult::DEVICE_FULL; | 46 result = SavePageResult::DEVICE_FULL; |
41 break; | 47 break; |
42 case ArchiverResult::ERROR_CONTENT_UNAVAILABLE: | 48 case ArchiverResult::ERROR_CONTENT_UNAVAILABLE: |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 if (iter == offline_pages_.end()) | 124 if (iter == offline_pages_.end()) |
119 return; | 125 return; |
120 | 126 |
121 // Make a copy of the cached item and update it. The cached item should only | 127 // Make a copy of the cached item and update it. The cached item should only |
122 // be updated upon the successful store operation. | 128 // be updated upon the successful store operation. |
123 OfflinePageItem offline_page_item = iter->second; | 129 OfflinePageItem offline_page_item = iter->second; |
124 offline_page_item.last_access_time = base::Time::Now(); | 130 offline_page_item.last_access_time = base::Time::Now(); |
125 offline_page_item.access_count++; | 131 offline_page_item.access_count++; |
126 store_->AddOrUpdateOfflinePage( | 132 store_->AddOrUpdateOfflinePage( |
127 offline_page_item, | 133 offline_page_item, |
128 base::Bind(&OfflinePageModel::OnUpdateOfflinePageDone, | 134 base::Bind(&OfflinePageModel::OnMarkPageAccesseDone, |
129 weak_ptr_factory_.GetWeakPtr(), offline_page_item)); | 135 weak_ptr_factory_.GetWeakPtr(), offline_page_item)); |
130 } | 136 } |
131 | 137 |
| 138 void OfflinePageModel::MarkPageForDeletion(int64 bookmark_id, |
| 139 const DeletePageCallback& callback) { |
| 140 DCHECK(is_loaded_); |
| 141 auto iter = offline_pages_.find(bookmark_id); |
| 142 if (iter == offline_pages_.end()) { |
| 143 InformDeletePageDone(callback, DeletePageResult::NOT_FOUND); |
| 144 return; |
| 145 } |
| 146 |
| 147 // Make a copy of the cached item and update it. The cached item should only |
| 148 // be updated upon the successful store operation. |
| 149 OfflinePageItem offline_page_item = iter->second; |
| 150 offline_page_item.MarkForDeletion(); |
| 151 store_->AddOrUpdateOfflinePage( |
| 152 offline_page_item, |
| 153 base::Bind(&OfflinePageModel::OnMarkPageForDeletionDone, |
| 154 weak_ptr_factory_.GetWeakPtr(), offline_page_item, callback)); |
| 155 } |
| 156 |
132 void OfflinePageModel::DeletePageByBookmarkId( | 157 void OfflinePageModel::DeletePageByBookmarkId( |
133 int64 bookmark_id, | 158 int64 bookmark_id, |
134 const DeletePageCallback& callback) { | 159 const DeletePageCallback& callback) { |
135 DCHECK(is_loaded_); | 160 DCHECK(is_loaded_); |
136 std::vector<int64> bookmark_ids_to_delete; | 161 std::vector<int64> bookmark_ids_to_delete; |
137 bookmark_ids_to_delete.push_back(bookmark_id); | 162 bookmark_ids_to_delete.push_back(bookmark_id); |
138 DeletePagesByBookmarkId(bookmark_ids_to_delete, callback); | 163 DeletePagesByBookmarkId(bookmark_ids_to_delete, callback); |
139 } | 164 } |
140 | 165 |
141 void OfflinePageModel::DeletePagesByBookmarkId( | 166 void OfflinePageModel::DeletePagesByBookmarkId( |
(...skipping 21 matching lines...) Expand all Loading... |
163 base::Bind(&OfflinePageModel::OnDeleteArchiveFilesDone, | 188 base::Bind(&OfflinePageModel::OnDeleteArchiveFilesDone, |
164 weak_ptr_factory_.GetWeakPtr(), | 189 weak_ptr_factory_.GetWeakPtr(), |
165 bookmark_ids, | 190 bookmark_ids, |
166 callback, | 191 callback, |
167 base::Owned(success))); | 192 base::Owned(success))); |
168 } | 193 } |
169 | 194 |
170 const std::vector<OfflinePageItem> OfflinePageModel::GetAllPages() const { | 195 const std::vector<OfflinePageItem> OfflinePageModel::GetAllPages() const { |
171 DCHECK(is_loaded_); | 196 DCHECK(is_loaded_); |
172 std::vector<OfflinePageItem> offline_pages; | 197 std::vector<OfflinePageItem> offline_pages; |
173 for (const auto& id_page_pair : offline_pages_) | 198 for (const auto& id_page_pair : offline_pages_) { |
| 199 if (id_page_pair.second.IsMarkedForDeletion()) |
| 200 continue; |
174 offline_pages.push_back(id_page_pair.second); | 201 offline_pages.push_back(id_page_pair.second); |
| 202 } |
175 return offline_pages; | 203 return offline_pages; |
176 } | 204 } |
177 | 205 |
178 const std::vector<OfflinePageItem> OfflinePageModel::GetPagesToCleanUp() const { | 206 const std::vector<OfflinePageItem> OfflinePageModel::GetPagesToCleanUp() const { |
179 DCHECK(is_loaded_); | 207 DCHECK(is_loaded_); |
180 std::vector<OfflinePageItem> offline_pages; | 208 std::vector<OfflinePageItem> offline_pages; |
181 base::Time now = base::Time::Now(); | 209 base::Time now = base::Time::Now(); |
182 for (const auto& id_page_pair : offline_pages_) { | 210 for (const auto& id_page_pair : offline_pages_) { |
183 if (now - id_page_pair.second.last_access_time > kPageCleanUpThreshold) | 211 if (!id_page_pair.second.IsMarkedForDeletion() && |
| 212 now - id_page_pair.second.last_access_time > kPageCleanUpThreshold) { |
184 offline_pages.push_back(id_page_pair.second); | 213 offline_pages.push_back(id_page_pair.second); |
| 214 } |
185 } | 215 } |
186 return offline_pages; | 216 return offline_pages; |
187 } | 217 } |
188 | 218 |
189 const OfflinePageItem* OfflinePageModel::GetPageByBookmarkId( | 219 const OfflinePageItem* OfflinePageModel::GetPageByBookmarkId( |
190 int64 bookmark_id) const { | 220 int64 bookmark_id) const { |
191 const auto iter = offline_pages_.find(bookmark_id); | 221 const auto iter = offline_pages_.find(bookmark_id); |
192 return iter != offline_pages_.end() ? &(iter->second) : nullptr; | 222 return iter != offline_pages_.end() ? &(iter->second) : nullptr; |
193 } | 223 } |
194 | 224 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 if (success) { | 278 if (success) { |
249 offline_pages_[offline_page.bookmark_id] = offline_page; | 279 offline_pages_[offline_page.bookmark_id] = offline_page; |
250 result = SavePageResult::SUCCESS; | 280 result = SavePageResult::SUCCESS; |
251 UMA_HISTOGRAM_MEMORY_KB( | 281 UMA_HISTOGRAM_MEMORY_KB( |
252 "OfflinePages.PageSize", offline_page.file_size / 1024); | 282 "OfflinePages.PageSize", offline_page.file_size / 1024); |
253 } else { | 283 } else { |
254 result = SavePageResult::STORE_FAILURE; | 284 result = SavePageResult::STORE_FAILURE; |
255 } | 285 } |
256 InformSavePageDone(callback, result); | 286 InformSavePageDone(callback, result); |
257 DeletePendingArchiver(archiver); | 287 DeletePendingArchiver(archiver); |
| 288 |
| 289 FOR_EACH_OBSERVER(Observer, observers_, OfflinePageModelChanged(this)); |
258 } | 290 } |
259 | 291 |
260 void OfflinePageModel::OnUpdateOfflinePageDone( | 292 void OfflinePageModel::OnMarkPageAccesseDone( |
261 const OfflinePageItem& offline_page_item, bool success) { | 293 const OfflinePageItem& offline_page_item, bool success) { |
262 // Update the item in the cache only upon success. | 294 // Update the item in the cache only upon success. |
263 if (success) | 295 if (success) |
264 offline_pages_[offline_page_item.bookmark_id] = offline_page_item; | 296 offline_pages_[offline_page_item.bookmark_id] = offline_page_item; |
| 297 |
| 298 // No need to fire OfflinePageModelChanged event since updating access info |
| 299 // should not have any impact to the UI. |
| 300 } |
| 301 |
| 302 void OfflinePageModel::OnMarkPageForDeletionDone( |
| 303 const OfflinePageItem& offline_page_item, |
| 304 const DeletePageCallback& callback, |
| 305 bool success) { |
| 306 // Update the item in the cache only upon success. |
| 307 if (success) |
| 308 offline_pages_[offline_page_item.bookmark_id] = offline_page_item; |
| 309 |
| 310 InformDeletePageDone(callback, success ? DeletePageResult::SUCCESS |
| 311 : DeletePageResult::STORE_FAILURE); |
| 312 |
| 313 if (!success) |
| 314 return; |
| 315 |
| 316 // Schedule to do the final deletion. |
| 317 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| 318 FROM_HERE, |
| 319 base::Bind(&OfflinePageModel::FinalizePageDeletion, |
| 320 weak_ptr_factory_.GetWeakPtr()), |
| 321 kFinalDeletionDelay); |
| 322 |
| 323 FOR_EACH_OBSERVER(Observer, observers_, OfflinePageModelChanged(this)); |
| 324 } |
| 325 |
| 326 void OfflinePageModel::OnUndoOfflinePageDone( |
| 327 const OfflinePageItem& offline_page, bool success) { |
| 328 if (!success) |
| 329 return; |
| 330 offline_pages_[offline_page.bookmark_id] = offline_page; |
| 331 |
| 332 FOR_EACH_OBSERVER(Observer, observers_, OfflinePageModelChanged(this)); |
| 333 } |
| 334 |
| 335 void OfflinePageModel::FinalizePageDeletion() { |
| 336 std::vector<int64> bookmark_ids_pending_deletion; |
| 337 for (const auto& id_page_pair : offline_pages_) { |
| 338 if (!id_page_pair.second.IsMarkedForDeletion()) |
| 339 continue; |
| 340 bookmark_ids_pending_deletion.push_back(id_page_pair.second.bookmark_id); |
| 341 } |
| 342 DeletePagesByBookmarkId(bookmark_ids_pending_deletion, DeletePageCallback()); |
| 343 } |
| 344 |
| 345 void OfflinePageModel::UndoPageDeletion(int64 bookmark_id) { |
| 346 auto iter = offline_pages_.find(bookmark_id); |
| 347 if (iter == offline_pages_.end()) |
| 348 return; |
| 349 |
| 350 // Make a copy of the cached item and update it. The cached item should only |
| 351 // be updated upon the successful store operation. |
| 352 OfflinePageItem offline_page_item = iter->second; |
| 353 if (!offline_page_item.IsMarkedForDeletion()) |
| 354 return; |
| 355 |
| 356 // Clear the flag to bring it back. |
| 357 offline_page_item.ClearMarkForDeletion(); |
| 358 store_->AddOrUpdateOfflinePage( |
| 359 offline_page_item, |
| 360 base::Bind(&OfflinePageModel::OnUndoOfflinePageDone, |
| 361 weak_ptr_factory_.GetWeakPtr(), offline_page_item)); |
265 } | 362 } |
266 | 363 |
267 void OfflinePageModel::BookmarkModelChanged() { | 364 void OfflinePageModel::BookmarkModelChanged() { |
268 } | 365 } |
269 | 366 |
| 367 void OfflinePageModel::BookmarkNodeAdded(bookmarks::BookmarkModel* model, |
| 368 const bookmarks::BookmarkNode* parent, |
| 369 int index) { |
| 370 const bookmarks::BookmarkNode* node = parent->GetChild(index); |
| 371 DCHECK(node); |
| 372 UndoPageDeletion(node->id()); |
| 373 } |
| 374 |
270 void OfflinePageModel::BookmarkNodeRemoved( | 375 void OfflinePageModel::BookmarkNodeRemoved( |
271 bookmarks::BookmarkModel* model, | 376 bookmarks::BookmarkModel* model, |
272 const bookmarks::BookmarkNode* parent, | 377 const bookmarks::BookmarkNode* parent, |
273 int old_index, | 378 int old_index, |
274 const bookmarks::BookmarkNode* node, | 379 const bookmarks::BookmarkNode* node, |
275 const std::set<GURL>& removed_urls) { | 380 const std::set<GURL>& removed_urls) { |
276 if (!is_loaded_) { | 381 if (!is_loaded_) { |
277 delayed_tasks_.push_back( | 382 delayed_tasks_.push_back( |
278 base::Bind(&OfflinePageModel::DeletePageByBookmarkId, | 383 base::Bind(&OfflinePageModel::MarkPageForDeletion, |
279 weak_ptr_factory_.GetWeakPtr(), | 384 weak_ptr_factory_.GetWeakPtr(), |
280 node->id(), | 385 node->id(), |
281 base::Bind(&EmptyDeleteCallback))); | 386 base::Bind(&EmptyDeleteCallback))); |
282 return; | 387 return; |
283 } | 388 } |
284 DeletePageByBookmarkId(node->id(), base::Bind(&EmptyDeleteCallback)); | 389 MarkPageForDeletion(node->id(), base::Bind(&EmptyDeleteCallback)); |
285 } | 390 } |
286 | 391 |
287 void OfflinePageModel::OnLoadDone( | 392 void OfflinePageModel::OnLoadDone( |
288 bool success, | 393 bool success, |
289 const std::vector<OfflinePageItem>& offline_pages) { | 394 const std::vector<OfflinePageItem>& offline_pages) { |
290 DCHECK(!is_loaded_); | 395 DCHECK(!is_loaded_); |
291 is_loaded_ = true; | 396 is_loaded_ = true; |
292 | 397 |
293 if (success) { | 398 if (success) { |
294 for (const auto& offline_page : offline_pages) | 399 for (const auto& offline_page : offline_pages) |
295 offline_pages_[offline_page.bookmark_id] = offline_page; | 400 offline_pages_[offline_page.bookmark_id] = offline_page; |
296 } | 401 } |
297 | 402 |
298 // Run all the delayed tasks. | 403 // Run all the delayed tasks. |
299 for (const auto& delayed_task : delayed_tasks_) | 404 for (const auto& delayed_task : delayed_tasks_) |
300 delayed_task.Run(); | 405 delayed_task.Run(); |
301 delayed_tasks_.clear(); | 406 delayed_tasks_.clear(); |
302 | 407 |
| 408 // If there are pages that are marked for deletion, but not yet deleted and |
| 409 // OfflinePageModel gets reloaded. Delete the pages now. |
| 410 FinalizePageDeletion(); |
| 411 |
303 FOR_EACH_OBSERVER(Observer, observers_, OfflinePageModelLoaded(this)); | 412 FOR_EACH_OBSERVER(Observer, observers_, OfflinePageModelLoaded(this)); |
304 } | 413 } |
305 | 414 |
306 void OfflinePageModel::InformSavePageDone(const SavePageCallback& callback, | 415 void OfflinePageModel::InformSavePageDone(const SavePageCallback& callback, |
307 SavePageResult result) { | 416 SavePageResult result) { |
308 UMA_HISTOGRAM_ENUMERATION( | 417 UMA_HISTOGRAM_ENUMERATION( |
309 "OfflinePages.SavePageResult", | 418 "OfflinePages.SavePageResult", |
310 static_cast<int>(result), | 419 static_cast<int>(result), |
311 static_cast<int>(SavePageResult::RESULT_COUNT)); | 420 static_cast<int>(SavePageResult::RESULT_COUNT)); |
312 callback.Run(result); | 421 callback.Run(result); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
358 (success || bookmark_ids.size() > 1) ? DeletePageResult::SUCCESS | 467 (success || bookmark_ids.size() > 1) ? DeletePageResult::SUCCESS |
359 : DeletePageResult::STORE_FAILURE); | 468 : DeletePageResult::STORE_FAILURE); |
360 } | 469 } |
361 | 470 |
362 void OfflinePageModel::InformDeletePageDone(const DeletePageCallback& callback, | 471 void OfflinePageModel::InformDeletePageDone(const DeletePageCallback& callback, |
363 DeletePageResult result) { | 472 DeletePageResult result) { |
364 UMA_HISTOGRAM_ENUMERATION( | 473 UMA_HISTOGRAM_ENUMERATION( |
365 "OfflinePages.DeletePageResult", | 474 "OfflinePages.DeletePageResult", |
366 static_cast<int>(result), | 475 static_cast<int>(result), |
367 static_cast<int>(DeletePageResult::RESULT_COUNT)); | 476 static_cast<int>(DeletePageResult::RESULT_COUNT)); |
368 callback.Run(result); | 477 if (!callback.is_null()) |
| 478 callback.Run(result); |
369 } | 479 } |
370 | 480 |
371 } // namespace offline_pages | 481 } // namespace offline_pages |
OLD | NEW |