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 |