| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <functional> | 8 #include <functional> |
| 9 #include <limits> | 9 #include <limits> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/compiler_specific.h" | 12 #include "base/compiler_specific.h" |
| 13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
| 14 #include "base/files/file_enumerator.h" | 14 #include "base/files/file_enumerator.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
| 17 #include "chrome/browser/bookmarks/bookmark_service.h" | 17 #include "chrome/browser/bookmarks/bookmark_service.h" |
| 18 #include "chrome/browser/chrome_notification_types.h" | 18 #include "chrome/browser/chrome_notification_types.h" |
| 19 #include "chrome/browser/history/archived_database.h" | |
| 20 #include "chrome/browser/history/history_database.h" | 19 #include "chrome/browser/history/history_database.h" |
| 21 #include "chrome/browser/history/history_notifications.h" | 20 #include "chrome/browser/history/history_notifications.h" |
| 22 #include "chrome/browser/history/thumbnail_database.h" | 21 #include "chrome/browser/history/thumbnail_database.h" |
| 23 | 22 |
| 24 using base::Time; | 23 using base::Time; |
| 25 using base::TimeDelta; | 24 using base::TimeDelta; |
| 26 | 25 |
| 27 namespace history { | 26 namespace history { |
| 28 | 27 |
| 29 namespace { | 28 namespace { |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 content::PAGE_TRANSITION_AUTO_SUBFRAME, | 86 content::PAGE_TRANSITION_AUTO_SUBFRAME, |
| 88 visits); | 87 visits); |
| 89 bool more = static_cast<int>(visits->size()) == max_visits; | 88 bool more = static_cast<int>(visits->size()) == max_visits; |
| 90 if (!more) | 89 if (!more) |
| 91 db->UpdateEarlyExpirationThreshold(early_end_time); | 90 db->UpdateEarlyExpirationThreshold(early_end_time); |
| 92 | 91 |
| 93 return more; | 92 return more; |
| 94 } | 93 } |
| 95 }; | 94 }; |
| 96 | 95 |
| 97 // Returns true if this visit is worth archiving. Otherwise, this visit is not | |
| 98 // worth saving (for example, subframe navigations and redirects) and we can | |
| 99 // just delete it when it gets old. | |
| 100 bool ShouldArchiveVisit(const VisitRow& visit) { | |
| 101 int no_qualifier = content::PageTransitionStripQualifier(visit.transition); | |
| 102 | |
| 103 // These types of transitions are always "important" and the user will want | |
| 104 // to see them. | |
| 105 if (no_qualifier == content::PAGE_TRANSITION_TYPED || | |
| 106 no_qualifier == content::PAGE_TRANSITION_AUTO_BOOKMARK || | |
| 107 no_qualifier == content::PAGE_TRANSITION_AUTO_TOPLEVEL) | |
| 108 return true; | |
| 109 | |
| 110 // Only archive these "less important" transitions when they were the final | |
| 111 // navigation and not part of a redirect chain. | |
| 112 if ((no_qualifier == content::PAGE_TRANSITION_LINK || | |
| 113 no_qualifier == content::PAGE_TRANSITION_FORM_SUBMIT || | |
| 114 no_qualifier == content::PAGE_TRANSITION_KEYWORD || | |
| 115 no_qualifier == content::PAGE_TRANSITION_GENERATED) && | |
| 116 visit.transition & content::PAGE_TRANSITION_CHAIN_END) | |
| 117 return true; | |
| 118 | |
| 119 // The transition types we ignore are AUTO_SUBFRAME and MANUAL_SUBFRAME. | |
| 120 return false; | |
| 121 } | |
| 122 | |
| 123 // The number of visits we will expire very time we check for old items. This | 96 // The number of visits we will expire very time we check for old items. This |
| 124 // Prevents us from doing too much work any given time. | 97 // Prevents us from doing too much work any given time. |
| 125 const int kNumExpirePerIteration = 32; | 98 const int kNumExpirePerIteration = 32; |
| 126 | 99 |
| 127 // The number of seconds between checking for items that should be expired when | 100 // The number of seconds between checking for items that should be expired when |
| 128 // we think there might be more items to expire. This timeout is used when the | 101 // we think there might be more items to expire. This timeout is used when the |
| 129 // last expiration found at least kNumExpirePerIteration and we want to check | 102 // last expiration found at least kNumExpirePerIteration and we want to check |
| 130 // again "soon." | 103 // again "soon." |
| 131 const int kExpirationDelaySec = 30; | 104 const int kExpirationDelaySec = 30; |
| 132 | 105 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 162 // The list of all favicon urls that were actually deleted from the thumbnail | 135 // The list of all favicon urls that were actually deleted from the thumbnail |
| 163 // db. | 136 // db. |
| 164 std::set<GURL> expired_favicons; | 137 std::set<GURL> expired_favicons; |
| 165 }; | 138 }; |
| 166 | 139 |
| 167 ExpireHistoryBackend::ExpireHistoryBackend( | 140 ExpireHistoryBackend::ExpireHistoryBackend( |
| 168 BroadcastNotificationDelegate* delegate, | 141 BroadcastNotificationDelegate* delegate, |
| 169 BookmarkService* bookmark_service) | 142 BookmarkService* bookmark_service) |
| 170 : delegate_(delegate), | 143 : delegate_(delegate), |
| 171 main_db_(NULL), | 144 main_db_(NULL), |
| 172 archived_db_(NULL), | |
| 173 thumb_db_(NULL), | 145 thumb_db_(NULL), |
| 174 weak_factory_(this), | 146 weak_factory_(this), |
| 175 bookmark_service_(bookmark_service) { | 147 bookmark_service_(bookmark_service) { |
| 176 } | 148 } |
| 177 | 149 |
| 178 ExpireHistoryBackend::~ExpireHistoryBackend() { | 150 ExpireHistoryBackend::~ExpireHistoryBackend() { |
| 179 } | 151 } |
| 180 | 152 |
| 181 void ExpireHistoryBackend::SetDatabases(HistoryDatabase* main_db, | 153 void ExpireHistoryBackend::SetDatabases(HistoryDatabase* main_db, |
| 182 ArchivedDatabase* archived_db, | |
| 183 ThumbnailDatabase* thumb_db) { | 154 ThumbnailDatabase* thumb_db) { |
| 184 main_db_ = main_db; | 155 main_db_ = main_db; |
| 185 archived_db_ = archived_db; | |
| 186 thumb_db_ = thumb_db; | 156 thumb_db_ = thumb_db; |
| 187 } | 157 } |
| 188 | 158 |
| 189 void ExpireHistoryBackend::DeleteURL(const GURL& url) { | 159 void ExpireHistoryBackend::DeleteURL(const GURL& url) { |
| 190 DeleteURLs(std::vector<GURL>(1, url)); | 160 DeleteURLs(std::vector<GURL>(1, url)); |
| 191 } | 161 } |
| 192 | 162 |
| 193 void ExpireHistoryBackend::DeleteURLs(const std::vector<GURL>& urls) { | 163 void ExpireHistoryBackend::DeleteURLs(const std::vector<GURL>& urls) { |
| 194 if (!main_db_) | 164 if (!main_db_) |
| 195 return; | 165 return; |
| 196 | 166 |
| 197 DeleteDependencies dependencies; | 167 DeleteDependencies dependencies; |
| 198 for (std::vector<GURL>::const_iterator url = urls.begin(); url != urls.end(); | 168 for (std::vector<GURL>::const_iterator url = urls.begin(); url != urls.end(); |
| 199 ++url) { | 169 ++url) { |
| 200 URLRow url_row; | 170 URLRow url_row; |
| 201 if (!main_db_->GetRowForURL(*url, &url_row)) | 171 if (!main_db_->GetRowForURL(*url, &url_row)) |
| 202 continue; // Nothing to delete. | 172 continue; // Nothing to delete. |
| 203 | 173 |
| 204 // Collect all the visits and delete them. Note that we don't give | 174 // Collect all the visits and delete them. Note that we don't give |
| 205 // up if there are no visits, since the URL could still have an | 175 // up if there are no visits, since the URL could still have an |
| 206 // entry that we should delete. TODO(brettw): bug 1171148: We | 176 // entry that we should delete. |
| 207 // should also delete from the archived DB. | |
| 208 VisitVector visits; | 177 VisitVector visits; |
| 209 main_db_->GetVisitsForURL(url_row.id(), &visits); | 178 main_db_->GetVisitsForURL(url_row.id(), &visits); |
| 210 | 179 |
| 211 DeleteVisitRelatedInfo(visits, &dependencies); | 180 DeleteVisitRelatedInfo(visits, &dependencies); |
| 212 | 181 |
| 213 // We skip ExpireURLsForVisits (since we are deleting from the | 182 // We skip ExpireURLsForVisits (since we are deleting from the |
| 214 // URL, and not starting with visits in a given time range). We | 183 // URL, and not starting with visits in a given time range). We |
| 215 // therefore need to call the deletion and favicon update | 184 // therefore need to call the deletion and favicon update |
| 216 // functions manually. | 185 // functions manually. |
| 217 | 186 |
| 218 BookmarkService* bookmark_service = GetBookmarkService(); | 187 BookmarkService* bookmark_service = GetBookmarkService(); |
| 219 bool is_bookmarked = | 188 bool is_bookmarked = |
| 220 (bookmark_service && bookmark_service->IsBookmarked(*url)); | 189 (bookmark_service && bookmark_service->IsBookmarked(*url)); |
| 221 | 190 |
| 222 DeleteOneURL(url_row, is_bookmarked, &dependencies); | 191 DeleteOneURL(url_row, is_bookmarked, &dependencies); |
| 223 } | 192 } |
| 224 | 193 |
| 225 DeleteFaviconsIfPossible(dependencies.affected_favicons, | 194 DeleteFaviconsIfPossible(dependencies.affected_favicons, |
| 226 &dependencies.expired_favicons); | 195 &dependencies.expired_favicons); |
| 227 | 196 |
| 228 BroadcastDeleteNotifications(&dependencies, DELETION_USER_INITIATED); | 197 BroadcastDeleteNotifications(&dependencies, DELETION_USER_INITIATED); |
| 229 } | 198 } |
| 230 | 199 |
| 231 void ExpireHistoryBackend::ExpireHistoryBetween( | 200 void ExpireHistoryBackend::ExpireHistoryBetween( |
| 232 const std::set<GURL>& restrict_urls, Time begin_time, Time end_time) { | 201 const std::set<GURL>& restrict_urls, Time begin_time, Time end_time) { |
| 233 if (!main_db_) | 202 if (!main_db_) |
| 234 return; | 203 return; |
| 235 | 204 |
| 236 // Find the affected visits and delete them. | 205 // Find the affected visits and delete them. |
| 237 // TODO(brettw): bug 1171164: We should query the archived database here, too. | |
| 238 VisitVector visits; | 206 VisitVector visits; |
| 239 main_db_->GetAllVisitsInRange(begin_time, end_time, 0, &visits); | 207 main_db_->GetAllVisitsInRange(begin_time, end_time, 0, &visits); |
| 240 if (!restrict_urls.empty()) { | 208 if (!restrict_urls.empty()) { |
| 241 std::set<URLID> url_ids; | 209 std::set<URLID> url_ids; |
| 242 for (std::set<GURL>::const_iterator url = restrict_urls.begin(); | 210 for (std::set<GURL>::const_iterator url = restrict_urls.begin(); |
| 243 url != restrict_urls.end(); ++url) | 211 url != restrict_urls.end(); ++url) |
| 244 url_ids.insert(main_db_->GetRowForURL(*url, NULL)); | 212 url_ids.insert(main_db_->GetRowForURL(*url, NULL)); |
| 245 VisitVector all_visits; | 213 VisitVector all_visits; |
| 246 all_visits.swap(visits); | 214 all_visits.swap(visits); |
| 247 for (VisitVector::iterator visit = all_visits.begin(); | 215 for (VisitVector::iterator visit = all_visits.begin(); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 260 // it. | 228 // it. |
| 261 DCHECK( | 229 DCHECK( |
| 262 std::adjacent_find( | 230 std::adjacent_find( |
| 263 times.begin(), times.end(), std::less_equal<base::Time>()) == | 231 times.begin(), times.end(), std::less_equal<base::Time>()) == |
| 264 times.end()); | 232 times.end()); |
| 265 | 233 |
| 266 if (!main_db_) | 234 if (!main_db_) |
| 267 return; | 235 return; |
| 268 | 236 |
| 269 // Find the affected visits and delete them. | 237 // Find the affected visits and delete them. |
| 270 // TODO(brettw): bug 1171164: We should query the archived database here, too. | |
| 271 VisitVector visits; | 238 VisitVector visits; |
| 272 main_db_->GetVisitsForTimes(times, &visits); | 239 main_db_->GetVisitsForTimes(times, &visits); |
| 273 ExpireVisits(visits); | 240 ExpireVisits(visits); |
| 274 } | 241 } |
| 275 | 242 |
| 276 void ExpireHistoryBackend::ExpireVisits(const VisitVector& visits) { | 243 void ExpireHistoryBackend::ExpireVisits(const VisitVector& visits) { |
| 277 if (visits.empty()) | 244 if (visits.empty()) |
| 278 return; | 245 return; |
| 279 | 246 |
| 280 DeleteDependencies dependencies; | 247 DeleteDependencies dependencies; |
| 281 DeleteVisitRelatedInfo(visits, &dependencies); | 248 DeleteVisitRelatedInfo(visits, &dependencies); |
| 282 | 249 |
| 283 // Delete or update the URLs affected. We want to update the visit counts | 250 // Delete or update the URLs affected. We want to update the visit counts |
| 284 // since this is called by the user who wants to delete their recent history, | 251 // since this is called by the user who wants to delete their recent history, |
| 285 // and we don't want to leave any evidence. | 252 // and we don't want to leave any evidence. |
| 286 ExpireURLsForVisits(visits, &dependencies); | 253 ExpireURLsForVisits(visits, &dependencies); |
| 287 DeleteFaviconsIfPossible(dependencies.affected_favicons, | 254 DeleteFaviconsIfPossible(dependencies.affected_favicons, |
| 288 &dependencies.expired_favicons); | 255 &dependencies.expired_favicons); |
| 289 | 256 |
| 290 // An is_null begin time means that all history should be deleted. | 257 // An is_null begin time means that all history should be deleted. |
| 291 BroadcastDeleteNotifications(&dependencies, DELETION_USER_INITIATED); | 258 BroadcastDeleteNotifications(&dependencies, DELETION_USER_INITIATED); |
| 292 | 259 |
| 293 // Pick up any bits possibly left over. | 260 // Pick up any bits possibly left over. |
| 294 ParanoidExpireHistory(); | 261 ParanoidExpireHistory(); |
| 295 } | 262 } |
| 296 | 263 |
| 297 void ExpireHistoryBackend::ArchiveHistoryBefore(Time end_time) { | 264 void ExpireHistoryBackend::ExpireHistoryBefore(Time end_time) { |
| 298 if (!main_db_) | 265 if (!main_db_) |
| 299 return; | 266 return; |
| 300 | 267 |
| 301 // Archive as much history as possible before the given date. | 268 // Expire all history before the given date. |
| 302 ArchiveSomeOldHistory(end_time, GetAllVisitsReader(), | 269 ExpireSomeOldHistory(end_time, GetAllVisitsReader(), |
| 303 std::numeric_limits<int>::max()); | 270 std::numeric_limits<int>::max()); |
| 304 ParanoidExpireHistory(); | 271 ParanoidExpireHistory(); |
| 305 } | 272 } |
| 306 | 273 |
| 307 void ExpireHistoryBackend::InitWorkQueue() { | 274 void ExpireHistoryBackend::InitWorkQueue() { |
| 308 DCHECK(work_queue_.empty()) << "queue has to be empty prior to init"; | 275 DCHECK(work_queue_.empty()) << "queue has to be empty prior to init"; |
| 309 | 276 |
| 310 for (size_t i = 0; i < readers_.size(); i++) | 277 for (size_t i = 0; i < readers_.size(); i++) |
| 311 work_queue_.push(readers_[i]); | 278 work_queue_.push(readers_[i]); |
| 312 } | 279 } |
| 313 | 280 |
| 314 const ExpiringVisitsReader* ExpireHistoryBackend::GetAllVisitsReader() { | 281 const ExpiringVisitsReader* ExpireHistoryBackend::GetAllVisitsReader() { |
| 315 if (!all_visits_reader_) | 282 if (!all_visits_reader_) |
| 316 all_visits_reader_.reset(new AllVisitsReader()); | 283 all_visits_reader_.reset(new AllVisitsReader()); |
| 317 return all_visits_reader_.get(); | 284 return all_visits_reader_.get(); |
| 318 } | 285 } |
| 319 | 286 |
| 320 const ExpiringVisitsReader* | 287 const ExpiringVisitsReader* |
| 321 ExpireHistoryBackend::GetAutoSubframeVisitsReader() { | 288 ExpireHistoryBackend::GetAutoSubframeVisitsReader() { |
| 322 if (!auto_subframe_visits_reader_) | 289 if (!auto_subframe_visits_reader_) |
| 323 auto_subframe_visits_reader_.reset(new AutoSubframeVisitsReader()); | 290 auto_subframe_visits_reader_.reset(new AutoSubframeVisitsReader()); |
| 324 return auto_subframe_visits_reader_.get(); | 291 return auto_subframe_visits_reader_.get(); |
| 325 } | 292 } |
| 326 | 293 |
| 327 void ExpireHistoryBackend::StartArchivingOldStuff( | 294 void ExpireHistoryBackend::StartExpiringOldStuff( |
| 328 TimeDelta expiration_threshold) { | 295 TimeDelta expiration_threshold) { |
| 329 expiration_threshold_ = expiration_threshold; | 296 expiration_threshold_ = expiration_threshold; |
| 330 | 297 |
| 331 // Remove all readers, just in case this was method was called before. | 298 // Remove all readers, just in case this was method was called before. |
| 332 readers_.clear(); | 299 readers_.clear(); |
| 333 // For now, we explicitly add all known readers. If we come up with more | 300 // For now, we explicitly add all known readers. If we come up with more |
| 334 // reader types (in case we want to expire different types of visits in | 301 // reader types (in case we want to expire different types of visits in |
| 335 // different ways), we can make it be populated by creator/owner of | 302 // different ways), we can make it be populated by creator/owner of |
| 336 // ExpireHistoryBackend. | 303 // ExpireHistoryBackend. |
| 337 readers_.push_back(GetAllVisitsReader()); | 304 readers_.push_back(GetAllVisitsReader()); |
| 338 readers_.push_back(GetAutoSubframeVisitsReader()); | 305 readers_.push_back(GetAutoSubframeVisitsReader()); |
| 339 | 306 |
| 340 // Initialize the queue with all tasks for the first set of iterations. | 307 // Initialize the queue with all tasks for the first set of iterations. |
| 341 InitWorkQueue(); | 308 InitWorkQueue(); |
| 342 ScheduleArchive(); | 309 ScheduleExpire(); |
| 343 } | 310 } |
| 344 | 311 |
| 345 void ExpireHistoryBackend::DeleteFaviconsIfPossible( | 312 void ExpireHistoryBackend::DeleteFaviconsIfPossible( |
| 346 const std::set<chrome::FaviconID>& favicon_set, | 313 const std::set<chrome::FaviconID>& favicon_set, |
| 347 std::set<GURL>* expired_favicons) { | 314 std::set<GURL>* expired_favicons) { |
| 348 if (!thumb_db_) | 315 if (!thumb_db_) |
| 349 return; | 316 return; |
| 350 | 317 |
| 351 for (std::set<chrome::FaviconID>::const_iterator i = favicon_set.begin(); | 318 for (std::set<chrome::FaviconID>::const_iterator i = favicon_set.begin(); |
| 352 i != favicon_set.end(); ++i) { | 319 i != favicon_set.end(); ++i) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 365 | 332 |
| 366 void ExpireHistoryBackend::BroadcastDeleteNotifications( | 333 void ExpireHistoryBackend::BroadcastDeleteNotifications( |
| 367 DeleteDependencies* dependencies, DeletionType type) { | 334 DeleteDependencies* dependencies, DeletionType type) { |
| 368 if (!dependencies->deleted_urls.empty()) { | 335 if (!dependencies->deleted_urls.empty()) { |
| 369 // Broadcast the URL deleted notification. Note that we also broadcast when | 336 // Broadcast the URL deleted notification. Note that we also broadcast when |
| 370 // we were requested to delete everything even if that was a NOP, since | 337 // we were requested to delete everything even if that was a NOP, since |
| 371 // some components care to know when history is deleted (it's up to them to | 338 // some components care to know when history is deleted (it's up to them to |
| 372 // determine if they care whether anything was deleted). | 339 // determine if they care whether anything was deleted). |
| 373 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails); | 340 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails); |
| 374 details->all_history = false; | 341 details->all_history = false; |
| 375 details->archived = (type == DELETION_ARCHIVED); | 342 details->expired = (type == DELETION_EXPIRED); |
| 376 details->rows = dependencies->deleted_urls; | 343 details->rows = dependencies->deleted_urls; |
| 377 details->favicon_urls = dependencies->expired_favicons; | 344 details->favicon_urls = dependencies->expired_favicons; |
| 378 delegate_->NotifySyncURLsDeleted(false, details->archived, &details->rows); | 345 delegate_->NotifySyncURLsDeleted(false, details->expired, &details->rows); |
| 379 delegate_->BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED, | 346 delegate_->BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED, |
| 380 details.PassAs<HistoryDetails>()); | 347 details.PassAs<HistoryDetails>()); |
| 381 } | 348 } |
| 382 } | 349 } |
| 383 | 350 |
| 384 void ExpireHistoryBackend::DeleteVisitRelatedInfo( | 351 void ExpireHistoryBackend::DeleteVisitRelatedInfo( |
| 385 const VisitVector& visits, | 352 const VisitVector& visits, |
| 386 DeleteDependencies* dependencies) { | 353 DeleteDependencies* dependencies) { |
| 387 for (size_t i = 0; i < visits.size(); i++) { | 354 for (size_t i = 0; i < visits.size(); i++) { |
| 388 // Delete the visit itself. | 355 // Delete the visit itself. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 } | 387 } |
| 421 // Delete the mapping entries for the url. | 388 // Delete the mapping entries for the url. |
| 422 thumb_db_->DeleteIconMappings(url_row.url()); | 389 thumb_db_->DeleteIconMappings(url_row.url()); |
| 423 } | 390 } |
| 424 } | 391 } |
| 425 // Last, delete the URL entry. | 392 // Last, delete the URL entry. |
| 426 main_db_->DeleteURLRow(url_row.id()); | 393 main_db_->DeleteURLRow(url_row.id()); |
| 427 } | 394 } |
| 428 } | 395 } |
| 429 | 396 |
| 430 URLID ExpireHistoryBackend::ArchiveOneURL(const URLRow& url_row) { | |
| 431 if (!archived_db_) | |
| 432 return 0; | |
| 433 | |
| 434 // See if this URL is present in the archived database already. Note that | |
| 435 // we must look up by ID since the URL ID will be different. | |
| 436 URLRow archived_row; | |
| 437 if (archived_db_->GetRowForURL(url_row.url(), &archived_row)) { | |
| 438 // TODO(sky): bug 1168470, need to archive past search terms. | |
| 439 // TODO(brettw): should be copy the visit counts over? This will mean that | |
| 440 // the main DB's visit counts are only for the last 3 months rather than | |
| 441 // accumulative. | |
| 442 archived_row.set_last_visit(url_row.last_visit()); | |
| 443 archived_db_->UpdateURLRow(archived_row.id(), archived_row); | |
| 444 return archived_row.id(); | |
| 445 } | |
| 446 | |
| 447 // This row is not in the archived DB, add it. | |
| 448 return archived_db_->AddURL(url_row); | |
| 449 } | |
| 450 | |
| 451 namespace { | 397 namespace { |
| 452 | 398 |
| 453 struct ChangedURL { | 399 struct ChangedURL { |
| 454 ChangedURL() : visit_count(0), typed_count(0) {} | 400 ChangedURL() : visit_count(0), typed_count(0) {} |
| 455 int visit_count; | 401 int visit_count; |
| 456 int typed_count; | 402 int typed_count; |
| 457 }; | 403 }; |
| 458 | 404 |
| 459 } // namespace | 405 } // namespace |
| 460 | 406 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 std::max(0, url_row.visit_count() - i->second.visit_count)); | 457 std::max(0, url_row.visit_count() - i->second.visit_count)); |
| 512 url_row.set_typed_count( | 458 url_row.set_typed_count( |
| 513 std::max(0, url_row.typed_count() - i->second.typed_count)); | 459 std::max(0, url_row.typed_count() - i->second.typed_count)); |
| 514 | 460 |
| 515 // Update the db with the new details. | 461 // Update the db with the new details. |
| 516 main_db_->UpdateURLRow(url_row.id(), url_row); | 462 main_db_->UpdateURLRow(url_row.id(), url_row); |
| 517 } | 463 } |
| 518 } | 464 } |
| 519 } | 465 } |
| 520 | 466 |
| 521 void ExpireHistoryBackend::ArchiveURLsAndVisits( | 467 void ExpireHistoryBackend::ScheduleExpire() { |
| 522 const VisitVector& visits, | |
| 523 DeleteDependencies* dependencies) { | |
| 524 if (!archived_db_ || !main_db_) | |
| 525 return; | |
| 526 | |
| 527 // Make sure all unique URL rows are added to the dependency list and the | |
| 528 // archived database. We will also keep the mapping between the main DB URLID | |
| 529 // and the archived one. | |
| 530 std::map<URLID, URLID> main_id_to_archived_id; | |
| 531 for (size_t i = 0; i < visits.size(); i++) { | |
| 532 std::map<URLID, URLRow>::const_iterator found = | |
| 533 dependencies->affected_urls.find(visits[i].url_id); | |
| 534 if (found == dependencies->affected_urls.end()) { | |
| 535 // Unique URL encountered, archive it. | |
| 536 URLRow row; // Row in the main DB. | |
| 537 URLID archived_id; // ID in the archived DB. | |
| 538 if (!main_db_->GetURLRow(visits[i].url_id, &row) || | |
| 539 !(archived_id = ArchiveOneURL(row))) { | |
| 540 // Failure archiving, skip this one. | |
| 541 continue; | |
| 542 } | |
| 543 | |
| 544 // Only add URL to the dependency list once we know we successfully | |
| 545 // archived it. | |
| 546 main_id_to_archived_id[row.id()] = archived_id; | |
| 547 dependencies->affected_urls[row.id()] = row; | |
| 548 } | |
| 549 } | |
| 550 | |
| 551 // Retrieve the sources for all the archived visits before archiving. | |
| 552 // The returned visit_sources vector should contain the source for each visit | |
| 553 // from visits at the same index. | |
| 554 VisitSourceMap visit_sources; | |
| 555 main_db_->GetVisitsSource(visits, &visit_sources); | |
| 556 | |
| 557 // Now archive the visits since we know the URL ID to make them reference. | |
| 558 // The source visit list should still reference the visits in the main DB, but | |
| 559 // we will update it to reflect only the visits that were successfully | |
| 560 // archived. | |
| 561 for (size_t i = 0; i < visits.size(); i++) { | |
| 562 // Construct the visit that we will add to the archived database. We do | |
| 563 // not store referring visits since we delete many of the visits when | |
| 564 // archiving. | |
| 565 VisitRow cur_visit(visits[i]); | |
| 566 cur_visit.url_id = main_id_to_archived_id[cur_visit.url_id]; | |
| 567 cur_visit.referring_visit = 0; | |
| 568 VisitSourceMap::iterator iter = visit_sources.find(visits[i].visit_id); | |
| 569 archived_db_->AddVisit( | |
| 570 &cur_visit, | |
| 571 iter == visit_sources.end() ? SOURCE_BROWSED : iter->second); | |
| 572 // Ignore failures, we will delete it from the main DB no matter what. | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 void ExpireHistoryBackend::ScheduleArchive() { | |
| 577 TimeDelta delay; | 468 TimeDelta delay; |
| 578 if (work_queue_.empty()) { | 469 if (work_queue_.empty()) { |
| 579 // If work queue is empty, reset the work queue to contain all tasks and | 470 // If work queue is empty, reset the work queue to contain all tasks and |
| 580 // schedule next iteration after a longer delay. | 471 // schedule next iteration after a longer delay. |
| 581 InitWorkQueue(); | 472 InitWorkQueue(); |
| 582 delay = TimeDelta::FromMinutes(kExpirationEmptyDelayMin); | 473 delay = TimeDelta::FromMinutes(kExpirationEmptyDelayMin); |
| 583 } else { | 474 } else { |
| 584 delay = TimeDelta::FromSeconds(kExpirationDelaySec); | 475 delay = TimeDelta::FromSeconds(kExpirationDelaySec); |
| 585 } | 476 } |
| 586 | 477 |
| 587 base::MessageLoop::current()->PostDelayedTask( | 478 base::MessageLoop::current()->PostDelayedTask( |
| 588 FROM_HERE, | 479 FROM_HERE, |
| 589 base::Bind(&ExpireHistoryBackend::DoArchiveIteration, | 480 base::Bind(&ExpireHistoryBackend::DoExpireIteration, |
| 590 weak_factory_.GetWeakPtr()), | 481 weak_factory_.GetWeakPtr()), |
| 591 delay); | 482 delay); |
| 592 } | 483 } |
| 593 | 484 |
| 594 void ExpireHistoryBackend::DoArchiveIteration() { | 485 void ExpireHistoryBackend::DoExpireIteration() { |
| 595 DCHECK(!work_queue_.empty()) << "queue has to be non-empty"; | 486 DCHECK(!work_queue_.empty()) << "queue has to be non-empty"; |
| 596 | 487 |
| 597 const ExpiringVisitsReader* reader = work_queue_.front(); | 488 const ExpiringVisitsReader* reader = work_queue_.front(); |
| 598 bool more_to_expire = ArchiveSomeOldHistory(GetCurrentArchiveTime(), reader, | 489 bool more_to_expire = ExpireSomeOldHistory(GetCurrentExpirationTime(), reader, |
| 599 kNumExpirePerIteration); | 490 kNumExpirePerIteration); |
| 600 | 491 |
| 601 work_queue_.pop(); | 492 work_queue_.pop(); |
| 602 // If there are more items to expire, add the reader back to the queue, thus | 493 // If there are more items to expire, add the reader back to the queue, thus |
| 603 // creating a new task for future iterations. | 494 // creating a new task for future iterations. |
| 604 if (more_to_expire) | 495 if (more_to_expire) |
| 605 work_queue_.push(reader); | 496 work_queue_.push(reader); |
| 606 | 497 |
| 607 ScheduleArchive(); | 498 ScheduleExpire(); |
| 608 } | 499 } |
| 609 | 500 |
| 610 bool ExpireHistoryBackend::ArchiveSomeOldHistory( | 501 bool ExpireHistoryBackend::ExpireSomeOldHistory( |
| 611 base::Time end_time, | 502 base::Time end_time, |
| 612 const ExpiringVisitsReader* reader, | 503 const ExpiringVisitsReader* reader, |
| 613 int max_visits) { | 504 int max_visits) { |
| 614 if (!main_db_) | 505 if (!main_db_) |
| 615 return false; | 506 return false; |
| 616 | 507 |
| 617 // Add an extra time unit to given end time, because | 508 // Add an extra time unit to given end time, because |
| 618 // GetAllVisitsInRange, et al. queries' end value is non-inclusive. | 509 // GetAllVisitsInRange, et al. queries' end value is non-inclusive. |
| 619 Time effective_end_time = | 510 Time effective_end_time = |
| 620 Time::FromInternalValue(end_time.ToInternalValue() + 1); | 511 Time::FromInternalValue(end_time.ToInternalValue() + 1); |
| 621 | 512 |
| 622 VisitVector affected_visits; | 513 VisitVector visits_to_delete; |
| 623 bool more_to_expire = reader->Read(effective_end_time, main_db_, | 514 bool more_to_expire = reader->Read(effective_end_time, main_db_, |
| 624 &affected_visits, max_visits); | 515 &visits_to_delete, max_visits); |
| 625 | 516 |
| 626 // Some visits we'll delete while others we'll archive. | 517 DeleteDependencies dependencies; |
| 627 VisitVector deleted_visits, archived_visits; | 518 DeleteVisitRelatedInfo(visits_to_delete, &dependencies); |
| 628 for (size_t i = 0; i < affected_visits.size(); i++) { | 519 ExpireURLsForVisits(visits_to_delete, &dependencies); |
| 629 if (ShouldArchiveVisit(affected_visits[i])) | 520 DeleteFaviconsIfPossible(dependencies.affected_favicons, |
| 630 archived_visits.push_back(affected_visits[i]); | 521 &dependencies.expired_favicons); |
| 631 else | |
| 632 deleted_visits.push_back(affected_visits[i]); | |
| 633 } | |
| 634 | 522 |
| 635 // Do the actual archiving. | 523 BroadcastDeleteNotifications(&dependencies, DELETION_EXPIRED); |
| 636 DeleteDependencies archived_dependencies; | |
| 637 ArchiveURLsAndVisits(archived_visits, &archived_dependencies); | |
| 638 DeleteVisitRelatedInfo(archived_visits, &archived_dependencies); | |
| 639 | |
| 640 DeleteDependencies deleted_dependencies; | |
| 641 DeleteVisitRelatedInfo(deleted_visits, &deleted_dependencies); | |
| 642 | |
| 643 // This will remove or archive all the affected URLs. Must do the deleting | |
| 644 // cleanup before archiving so the delete dependencies structure references | |
| 645 // only those URLs that were actually deleted instead of having some visits | |
| 646 // archived and then the rest deleted. | |
| 647 ExpireURLsForVisits(deleted_visits, &deleted_dependencies); | |
| 648 ExpireURLsForVisits(archived_visits, &archived_dependencies); | |
| 649 | |
| 650 // Create a union of all affected favicons (we don't store favicons for | |
| 651 // archived URLs) and delete them. | |
| 652 std::set<chrome::FaviconID> affected_favicons( | |
| 653 archived_dependencies.affected_favicons); | |
| 654 for (std::set<chrome::FaviconID>::const_iterator i = | |
| 655 deleted_dependencies.affected_favicons.begin(); | |
| 656 i != deleted_dependencies.affected_favicons.end(); ++i) { | |
| 657 affected_favicons.insert(*i); | |
| 658 } | |
| 659 DeleteFaviconsIfPossible(affected_favicons, | |
| 660 &deleted_dependencies.expired_favicons); | |
| 661 | |
| 662 // Send notifications for the stuff that was deleted. These won't normally be | |
| 663 // in history views since they were subframes, but they will be in the visited | |
| 664 // link system, which needs to be updated now. This function is smart enough | |
| 665 // to not do anything if nothing was deleted. | |
| 666 BroadcastDeleteNotifications(&deleted_dependencies, DELETION_ARCHIVED); | |
| 667 | 524 |
| 668 return more_to_expire; | 525 return more_to_expire; |
| 669 } | 526 } |
| 670 | 527 |
| 671 void ExpireHistoryBackend::ParanoidExpireHistory() { | 528 void ExpireHistoryBackend::ParanoidExpireHistory() { |
| 672 // TODO(brettw): Bug 1067331: write this to clean up any errors. | 529 // TODO(brettw): Bug 1067331: write this to clean up any errors. |
| 673 } | 530 } |
| 674 | 531 |
| 675 BookmarkService* ExpireHistoryBackend::GetBookmarkService() { | 532 BookmarkService* ExpireHistoryBackend::GetBookmarkService() { |
| 676 // We use the bookmark service to determine if a URL is bookmarked. The | 533 // We use the bookmark service to determine if a URL is bookmarked. The |
| 677 // bookmark service is loaded on a separate thread and may not be done by the | 534 // bookmark service is loaded on a separate thread and may not be done by the |
| 678 // time we get here. We therefor block until the bookmarks have finished | 535 // time we get here. We therefor block until the bookmarks have finished |
| 679 // loading. | 536 // loading. |
| 680 if (bookmark_service_) | 537 if (bookmark_service_) |
| 681 bookmark_service_->BlockTillLoaded(); | 538 bookmark_service_->BlockTillLoaded(); |
| 682 return bookmark_service_; | 539 return bookmark_service_; |
| 683 } | 540 } |
| 684 | 541 |
| 685 } // namespace history | 542 } // namespace history |
| OLD | NEW |