OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 "chrome/browser/history/expire_history_backend.h" | 5 #include "chrome/browser/history/expire_history_backend.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <limits> | 8 #include <limits> |
9 | 9 |
10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
12 #include "chrome/browser/bookmarks/bookmark_service.h" | 12 #include "chrome/browser/bookmarks/bookmark_service.h" |
13 #include "chrome/browser/history/archived_database.h" | 13 #include "chrome/browser/history/archived_database.h" |
14 #include "chrome/browser/history/history_database.h" | 14 #include "chrome/browser/history/history_database.h" |
15 #include "chrome/browser/history/history_notifications.h" | 15 #include "chrome/browser/history/history_notifications.h" |
16 #include "chrome/browser/history/text_database_manager.h" | 16 #include "chrome/browser/history/text_database_manager.h" |
17 #include "chrome/browser/history/thumbnail_database.h" | 17 #include "chrome/browser/history/thumbnail_database.h" |
18 #include "chrome/common/notification_type.h" | 18 #include "chrome/common/notification_type.h" |
19 | 19 |
20 using base::Time; | 20 using base::Time; |
21 using base::TimeDelta; | 21 using base::TimeDelta; |
22 | 22 |
23 namespace history { | 23 namespace history { |
24 | 24 |
25 namespace { | 25 namespace { |
26 | 26 |
| 27 // The number of days by which the expiration threshold is advanced for items |
| 28 // that we want to expire early, such as those of AUTO_SUBFRAME transition type. |
| 29 const int kEarlyExpirationAdvanceDays = 30; |
| 30 |
| 31 // Reads all types of visits starting from beginning of time to the given end |
| 32 // time. This is the most general reader. |
| 33 class AllVisitsReader : public ExpiringVisitsReader { |
| 34 public: |
| 35 virtual bool Read(Time end_time, HistoryDatabase* db, |
| 36 VisitVector* visits, int max_visits) const { |
| 37 DCHECK(db) << "must have a database to operate upon"; |
| 38 DCHECK(visits) << "visit vector has to exist in order to populate it"; |
| 39 |
| 40 db->GetAllVisitsInRange(Time(), end_time, max_visits, visits); |
| 41 // When we got the maximum number of visits we asked for, we say there could |
| 42 // be additional things to expire now. |
| 43 return static_cast<int>(visits->size()) == max_visits; |
| 44 } |
| 45 }; |
| 46 |
| 47 // Reads only AUTO_SUBFRAME visits, within a computed range. The range is |
| 48 // computed as follows: |
| 49 // * |begin_time| is read from the meta table. This value is updated whenever |
| 50 // there are no more additional visits to expire by this reader. |
| 51 // * |end_time| is advanced forward by a constant (kEarlyExpirationAdvanceDay), |
| 52 // but not past the current time. |
| 53 class AutoSubframeVisitsReader : public ExpiringVisitsReader { |
| 54 public: |
| 55 virtual bool Read(Time end_time, HistoryDatabase* db, |
| 56 VisitVector* visits, int max_visits) const { |
| 57 DCHECK(db) << "must have a database to operate upon"; |
| 58 DCHECK(visits) << "visit vector has to exist in order to populate it"; |
| 59 |
| 60 Time begin_time = db->GetEarlyExpirationThreshold(); |
| 61 // Advance |end_time| to expire early. |
| 62 Time early_end_time = end_time + |
| 63 TimeDelta::FromDays(kEarlyExpirationAdvanceDays); |
| 64 |
| 65 // We don't want to set the early expiration threshold to a time in the |
| 66 // future. |
| 67 Time now = Time::Now(); |
| 68 if (early_end_time > now) |
| 69 early_end_time = now; |
| 70 |
| 71 db->GetVisitsInRangeForTransition(begin_time, early_end_time, |
| 72 max_visits, |
| 73 PageTransition::AUTO_SUBFRAME, |
| 74 visits); |
| 75 bool more = static_cast<int>(visits->size()) == max_visits; |
| 76 if (!more) |
| 77 db->UpdateEarlyExpirationThreshold(early_end_time); |
| 78 |
| 79 return more; |
| 80 } |
| 81 }; |
| 82 |
27 // Returns true if this visit is worth archiving. Otherwise, this visit is not | 83 // Returns true if this visit is worth archiving. Otherwise, this visit is not |
28 // worth saving (for example, subframe navigations and redirects) and we can | 84 // worth saving (for example, subframe navigations and redirects) and we can |
29 // just delete it when it gets old. | 85 // just delete it when it gets old. |
30 bool ShouldArchiveVisit(const VisitRow& visit) { | 86 bool ShouldArchiveVisit(const VisitRow& visit) { |
31 int no_qualifier = PageTransition::StripQualifier(visit.transition); | 87 int no_qualifier = PageTransition::StripQualifier(visit.transition); |
32 | 88 |
33 // These types of transitions are always "important" and the user will want | 89 // These types of transitions are always "important" and the user will want |
34 // to see them. | 90 // to see them. |
35 if (no_qualifier == PageTransition::TYPED || | 91 if (no_qualifier == PageTransition::TYPED || |
36 no_qualifier == PageTransition::AUTO_BOOKMARK || | 92 no_qualifier == PageTransition::AUTO_BOOKMARK || |
(...skipping 14 matching lines...) Expand all Loading... |
51 } | 107 } |
52 | 108 |
53 // The number of visits we will expire very time we check for old items. This | 109 // The number of visits we will expire very time we check for old items. This |
54 // Prevents us from doing too much work any given time. | 110 // Prevents us from doing too much work any given time. |
55 const int kNumExpirePerIteration = 10; | 111 const int kNumExpirePerIteration = 10; |
56 | 112 |
57 // The number of seconds between checking for items that should be expired when | 113 // The number of seconds between checking for items that should be expired when |
58 // we think there might be more items to expire. This timeout is used when the | 114 // we think there might be more items to expire. This timeout is used when the |
59 // last expiration found at least kNumExpirePerIteration and we want to check | 115 // last expiration found at least kNumExpirePerIteration and we want to check |
60 // again "soon." | 116 // again "soon." |
61 const int kExpirationDelaySec = 60; | 117 const int kExpirationDelaySec = 30; |
62 | 118 |
63 // The number of minutes between checking, as with kExpirationDelaySec, but | 119 // The number of minutes between checking, as with kExpirationDelaySec, but |
64 // when we didn't find enough things to expire last time. If there was no | 120 // when we didn't find enough things to expire last time. If there was no |
65 // history to expire last iteration, it's likely there is nothing next | 121 // history to expire last iteration, it's likely there is nothing next |
66 // iteration, so we want to wait longer before checking to avoid wasting CPU. | 122 // iteration, so we want to wait longer before checking to avoid wasting CPU. |
67 const int kExpirationEmptyDelayMin = 5; | 123 const int kExpirationEmptyDelayMin = 5; |
68 | 124 |
69 } // namespace | 125 } // namespace |
70 | 126 |
71 ExpireHistoryBackend::ExpireHistoryBackend( | 127 ExpireHistoryBackend::ExpireHistoryBackend( |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
159 | 215 |
160 // Pick up any bits possibly left over. | 216 // Pick up any bits possibly left over. |
161 ParanoidExpireHistory(); | 217 ParanoidExpireHistory(); |
162 } | 218 } |
163 | 219 |
164 void ExpireHistoryBackend::ArchiveHistoryBefore(Time end_time) { | 220 void ExpireHistoryBackend::ArchiveHistoryBefore(Time end_time) { |
165 if (!main_db_) | 221 if (!main_db_) |
166 return; | 222 return; |
167 | 223 |
168 // Archive as much history as possible before the given date. | 224 // Archive as much history as possible before the given date. |
169 ArchiveSomeOldHistory(end_time, std::numeric_limits<size_t>::max()); | 225 ArchiveSomeOldHistory(end_time, GetAllVisitsReader(), |
| 226 std::numeric_limits<size_t>::max()); |
170 ParanoidExpireHistory(); | 227 ParanoidExpireHistory(); |
171 } | 228 } |
172 | 229 |
| 230 void ExpireHistoryBackend::InitWorkQueue() { |
| 231 DCHECK(work_queue_.empty()) << "queue has to be empty prior to init"; |
| 232 |
| 233 for (size_t i = 0; i < readers_.size(); i++) |
| 234 work_queue_.push(readers_[i]); |
| 235 } |
| 236 |
| 237 const ExpiringVisitsReader* ExpireHistoryBackend::GetAllVisitsReader() { |
| 238 if (!all_visits_reader_.get()) |
| 239 all_visits_reader_.reset(new AllVisitsReader()); |
| 240 return all_visits_reader_.get(); |
| 241 } |
| 242 |
| 243 const ExpiringVisitsReader* |
| 244 ExpireHistoryBackend::GetAutoSubframeVisitsReader() { |
| 245 if (!auto_subframe_visits_reader_.get()) |
| 246 auto_subframe_visits_reader_.reset(new AutoSubframeVisitsReader()); |
| 247 return auto_subframe_visits_reader_.get(); |
| 248 } |
| 249 |
173 void ExpireHistoryBackend::StartArchivingOldStuff( | 250 void ExpireHistoryBackend::StartArchivingOldStuff( |
174 TimeDelta expiration_threshold) { | 251 TimeDelta expiration_threshold) { |
175 expiration_threshold_ = expiration_threshold; | 252 expiration_threshold_ = expiration_threshold; |
176 ScheduleArchive(TimeDelta::FromSeconds(kExpirationDelaySec)); | 253 |
| 254 // Remove all readers, just in case this was method was called before. |
| 255 readers_.clear(); |
| 256 // For now, we explicitly add all known readers. If we come up with more |
| 257 // reader types (in case we want to expire different types of visits in |
| 258 // different ways), we can make it be populated by creator/owner of |
| 259 // ExpireHistoryBackend. |
| 260 readers_.push_back(GetAllVisitsReader()); |
| 261 readers_.push_back(GetAutoSubframeVisitsReader()); |
| 262 |
| 263 // Initialize the queue with all tasks for the first set of iterations. |
| 264 InitWorkQueue(); |
| 265 ScheduleArchive(); |
177 } | 266 } |
178 | 267 |
179 void ExpireHistoryBackend::DeleteFaviconsIfPossible( | 268 void ExpireHistoryBackend::DeleteFaviconsIfPossible( |
180 const std::set<FavIconID>& favicon_set) { | 269 const std::set<FavIconID>& favicon_set) { |
181 if (!main_db_ || !thumb_db_) | 270 if (!main_db_ || !thumb_db_) |
182 return; | 271 return; |
183 | 272 |
184 for (std::set<FavIconID>::const_iterator i = favicon_set.begin(); | 273 for (std::set<FavIconID>::const_iterator i = favicon_set.begin(); |
185 i != favicon_set.end(); ++i) { | 274 i != favicon_set.end(); ++i) { |
186 if (!main_db_->IsFavIconUsed(*i)) | 275 if (!main_db_->IsFavIconUsed(*i)) |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 // not store referring visits since we delete many of the visits when | 495 // not store referring visits since we delete many of the visits when |
407 // archiving. | 496 // archiving. |
408 VisitRow cur_visit(visits[i]); | 497 VisitRow cur_visit(visits[i]); |
409 cur_visit.url_id = main_id_to_archived_id[cur_visit.url_id]; | 498 cur_visit.url_id = main_id_to_archived_id[cur_visit.url_id]; |
410 cur_visit.referring_visit = 0; | 499 cur_visit.referring_visit = 0; |
411 archived_db_->AddVisit(&cur_visit); | 500 archived_db_->AddVisit(&cur_visit); |
412 // Ignore failures, we will delete it from the main DB no matter what. | 501 // Ignore failures, we will delete it from the main DB no matter what. |
413 } | 502 } |
414 } | 503 } |
415 | 504 |
416 void ExpireHistoryBackend::ScheduleArchive(TimeDelta delay) { | 505 void ExpireHistoryBackend::ScheduleArchive() { |
| 506 TimeDelta delay; |
| 507 if (work_queue_.empty()) { |
| 508 // If work queue is empty, reset the work queue to contain all tasks and |
| 509 // schedule next iteration after a longer delay. |
| 510 InitWorkQueue(); |
| 511 delay = TimeDelta::FromMinutes(kExpirationEmptyDelayMin); |
| 512 } else { |
| 513 delay = TimeDelta::FromSeconds(kExpirationDelaySec); |
| 514 } |
| 515 |
417 factory_.RevokeAll(); | 516 factory_.RevokeAll(); |
418 MessageLoop::current()->PostDelayedTask(FROM_HERE, factory_.NewRunnableMethod( | 517 MessageLoop::current()->PostDelayedTask(FROM_HERE, factory_.NewRunnableMethod( |
419 &ExpireHistoryBackend::DoArchiveIteration), delay.InMilliseconds()); | 518 &ExpireHistoryBackend::DoArchiveIteration), delay.InMilliseconds()); |
420 } | 519 } |
421 | 520 |
422 void ExpireHistoryBackend::DoArchiveIteration() { | 521 void ExpireHistoryBackend::DoArchiveIteration() { |
423 DCHECK(expiration_threshold_ != TimeDelta()) << "threshold should be set"; | 522 DCHECK(!work_queue_.empty()) << "queue has to be non-empty"; |
424 Time threshold = Time::Now() - expiration_threshold_; | |
425 | 523 |
426 if (ArchiveSomeOldHistory(threshold, kNumExpirePerIteration)) { | 524 const ExpiringVisitsReader* reader = work_queue_.front(); |
427 // Possibly more items to delete now, schedule it sooner to happen again. | 525 bool more_to_expire = ArchiveSomeOldHistory(GetCurrentArchiveTime(), reader, |
428 ScheduleArchive(TimeDelta::FromSeconds(kExpirationDelaySec)); | 526 kNumExpirePerIteration); |
429 } else { | 527 |
430 // If we didn't find the maximum number of items to delete, wait longer | 528 work_queue_.pop(); |
431 // before trying to delete more later. | 529 // If there are more items to expire, add the reader back to the queue, thus |
432 ScheduleArchive(TimeDelta::FromMinutes(kExpirationEmptyDelayMin)); | 530 // creating a new task for future iterations. |
433 } | 531 if (more_to_expire) |
| 532 work_queue_.push(reader); |
| 533 |
| 534 ScheduleArchive(); |
434 } | 535 } |
435 | 536 |
436 bool ExpireHistoryBackend::ArchiveSomeOldHistory(Time time_threshold, | 537 bool ExpireHistoryBackend::ArchiveSomeOldHistory( |
437 int max_visits) { | 538 base::Time end_time, |
| 539 const ExpiringVisitsReader* reader, |
| 540 int max_visits) { |
438 if (!main_db_) | 541 if (!main_db_) |
439 return false; | 542 return false; |
440 | 543 |
441 // Get all visits up to and including the threshold. This is a little tricky | 544 // Add an extra time unit to given end time, because |
442 // because GetAllVisitsInRange's end value is non-inclusive, so we have to | 545 // GetAllVisitsInRange, et al. queries' end value is non-inclusive. |
443 // increment the time by one unit to get the input value to be inclusive. | 546 Time effective_end_time = |
444 DCHECK(!time_threshold.is_null()); | 547 Time::FromInternalValue(end_time.ToInternalValue() + 1); |
445 Time effective_threshold = | 548 |
446 Time::FromInternalValue(time_threshold.ToInternalValue() + 1); | |
447 VisitVector affected_visits; | 549 VisitVector affected_visits; |
448 main_db_->GetAllVisitsInRange(Time(), effective_threshold, max_visits, | 550 bool more_to_expire = reader->Read(effective_end_time, main_db_, |
449 &affected_visits); | 551 &affected_visits, max_visits); |
450 | 552 |
451 // Some visits we'll delete while others we'll archive. | 553 // Some visits we'll delete while others we'll archive. |
452 VisitVector deleted_visits, archived_visits; | 554 VisitVector deleted_visits, archived_visits; |
453 for (size_t i = 0; i < affected_visits.size(); i++) { | 555 for (size_t i = 0; i < affected_visits.size(); i++) { |
454 if (ShouldArchiveVisit(affected_visits[i])) | 556 if (ShouldArchiveVisit(affected_visits[i])) |
455 archived_visits.push_back(affected_visits[i]); | 557 archived_visits.push_back(affected_visits[i]); |
456 else | 558 else |
457 deleted_visits.push_back(affected_visits[i]); | 559 deleted_visits.push_back(affected_visits[i]); |
458 } | 560 } |
459 | 561 |
(...skipping 22 matching lines...) Expand all Loading... |
482 affected_favicons.insert(*i); | 584 affected_favicons.insert(*i); |
483 } | 585 } |
484 DeleteFaviconsIfPossible(affected_favicons); | 586 DeleteFaviconsIfPossible(affected_favicons); |
485 | 587 |
486 // Send notifications for the stuff that was deleted. These won't normally be | 588 // Send notifications for the stuff that was deleted. These won't normally be |
487 // in history views since they were subframes, but they will be in the visited | 589 // in history views since they were subframes, but they will be in the visited |
488 // link system, which needs to be updated now. This function is smart enough | 590 // link system, which needs to be updated now. This function is smart enough |
489 // to not do anything if nothing was deleted. | 591 // to not do anything if nothing was deleted. |
490 BroadcastDeleteNotifications(&deleted_dependencies); | 592 BroadcastDeleteNotifications(&deleted_dependencies); |
491 | 593 |
492 // When we got the maximum number of visits we asked for, we say there could | 594 return more_to_expire; |
493 // be additional things to expire now. | |
494 return static_cast<int>(affected_visits.size()) == max_visits; | |
495 } | 595 } |
496 | 596 |
497 void ExpireHistoryBackend::ParanoidExpireHistory() { | 597 void ExpireHistoryBackend::ParanoidExpireHistory() { |
498 // FIXME(brettw): Bug 1067331: write this to clean up any errors. | 598 // FIXME(brettw): Bug 1067331: write this to clean up any errors. |
499 } | 599 } |
500 | 600 |
501 BookmarkService* ExpireHistoryBackend::GetBookmarkService() { | 601 BookmarkService* ExpireHistoryBackend::GetBookmarkService() { |
502 // We use the bookmark service to determine if a URL is bookmarked. The | 602 // We use the bookmark service to determine if a URL is bookmarked. The |
503 // bookmark service is loaded on a separate thread and may not be done by the | 603 // bookmark service is loaded on a separate thread and may not be done by the |
504 // time we get here. We therefor block until the bookmarks have finished | 604 // time we get here. We therefor block until the bookmarks have finished |
505 // loading. | 605 // loading. |
506 if (bookmark_service_) | 606 if (bookmark_service_) |
507 bookmark_service_->BlockTillLoaded(); | 607 bookmark_service_->BlockTillLoaded(); |
508 return bookmark_service_; | 608 return bookmark_service_; |
509 } | 609 } |
510 | 610 |
511 } // namespace history | 611 } // namespace history |
OLD | NEW |