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