history_backend.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/history/history_backend.h"
6
7#include <set>
8
9#include "base/command_line.h"
10#include "base/compiler_specific.h"
11#include "base/file_util.h"
12#include "base/histogram.h"
13#include "base/message_loop.h"
14#include "base/scoped_ptr.h"
15#include "base/scoped_vector.h"
16#include "base/string_util.h"
17#include "base/time.h"
18#include "chrome/browser/autocomplete/history_url_provider.h"
19#include "chrome/browser/bookmarks/bookmark_service.h"
20#include "chrome/browser/history/download_types.h"
21#include "chrome/browser/history/history_notifications.h"
22#include "chrome/browser/history/history_publisher.h"
23#include "chrome/browser/history/in_memory_history_backend.h"
24#include "chrome/browser/history/page_usage_data.h"
25#include "chrome/common/chrome_constants.h"
26#include "chrome/common/chrome_switches.h"
27#include "chrome/common/notification_type.h"
28#include "chrome/common/url_constants.h"
29#include "googleurl/src/gurl.h"
30#include "grit/chromium_strings.h"
31#include "grit/generated_resources.h"
32#include "net/base/registry_controlled_domain.h"
33
34using base::Time;
35using base::TimeDelta;
36using base::TimeTicks;
37
38/* The HistoryBackend consists of a number of components:
39
40    HistoryDatabase (stores past 3 months of history)
41      URLDatabase (stores a list of URLs)
42      DownloadDatabase (stores a list of downloads)
43      VisitDatabase (stores a list of visits for the URLs)
44      VisitSegmentDatabase (stores groups of URLs for the most visited view).
45
46    ArchivedDatabase (stores history older than 3 months)
47      URLDatabase (stores a list of URLs)
48      DownloadDatabase (stores a list of downloads)
49      VisitDatabase (stores a list of visits for the URLs)
50
51      (this does not store visit segments as they expire after 3 mos.)
52
53    TextDatabaseManager (manages multiple text database for different times)
54      TextDatabase (represents a single month of full-text index).
55      ...more TextDatabase objects...
56
57    ExpireHistoryBackend (manages moving things from HistoryDatabase to
58                          the ArchivedDatabase and deleting)
59*/
60
61namespace history {
62
63// How long we keep segment data for in days. Currently 3 months.
64// This value needs to be greater or equal to
65// MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct
66// dependency between MostVisitedModel and the history backend.
67static const int kSegmentDataRetention = 90;
68
69// The number of milliseconds we'll wait to do a commit, so that things are
70// batched together.
71static const int kCommitIntervalMs = 10000;
72
73// The amount of time before we re-fetch the favicon.
74static const int kFavIconRefetchDays = 7;
75
76// GetSessionTabs returns all open tabs, or tabs closed kSessionCloseTimeWindow
77// seconds ago.
78static const int kSessionCloseTimeWindowSecs = 10;
79
80// The maximum number of items we'll allow in the redirect list before
81// deleting some.
82static const int kMaxRedirectCount = 32;
83
84// The number of days old a history entry can be before it is considered "old"
85// and is archived.
86static const int kArchiveDaysThreshold = 90;
87
88// Converts from PageUsageData to MostVisitedURL. |redirects| is a
89// list of redirects for this URL. Empty list means no redirects.
90MostVisitedURL MakeMostVisitedURL(const PageUsageData& page_data,
91                                           const RedirectList& redirects) {
92  MostVisitedURL mv;
93  mv.url = page_data.GetURL();
94  mv.title = page_data.GetTitle();
95  if (redirects.empty()) {
96    // Redirects must contain at least the target url.
97    mv.redirects.push_back(mv.url);
98  } else {
99    mv.redirects = redirects;
100    if (mv.redirects[mv.redirects.size() - 1] != mv.url) {
101      // The last url must be the target url.
102      mv.redirects.push_back(mv.url);
103    }
104  }
105  return mv;
106}
107
108// This task is run on a timer so that commits happen at regular intervals
109// so they are batched together. The important thing about this class is that
110// it supports canceling of the task so the reference to the backend will be
111// freed. The problem is that when history is shutting down, there is likely
112// to be one of these commits still pending and holding a reference.
113//
114// The backend can call Cancel to have this task release the reference. The
115// task will still run (if we ever get to processing the event before
116// shutdown), but it will not do anything.
117//
118// Note that this is a refcounted object and is not a task in itself. It should
119// be assigned to a RunnableMethod.
120//
121// TODO(brettw): bug 1165182: This should be replaced with a
122// ScopedRunnableMethodFactory which will handle everything automatically (like
123// we do in ExpireHistoryBackend).
124class CommitLaterTask : public base::RefCounted<CommitLaterTask> {
125 public:
126  explicit CommitLaterTask(HistoryBackend* history_backend)
127      : history_backend_(history_backend) {
128  }
129
130  // The backend will call this function if it is being destroyed so that we
131  // release our reference.
132  void Cancel() {
133    history_backend_ = NULL;
134  }
135
136  void RunCommit() {
137    if (history_backend_.get())
138      history_backend_->Commit();
139  }
140
141 private:
142  friend class base::RefCounted<CommitLaterTask>;
143
144  ~CommitLaterTask() {}
145
146  scoped_refptr<HistoryBackend> history_backend_;
147};
148
149// Handles querying first the main database, then the full text database if that
150// fails. It will optionally keep track of all URLs seen so duplicates can be
151// eliminated. This is used by the querying sub-functions.
152//
153// TODO(brettw): This class may be able to be simplified or eliminated. After
154// this was written, QueryResults can efficiently look up by URL, so the need
155// for this extra set of previously queried URLs is less important.
156class HistoryBackend::URLQuerier {
157 public:
158  URLQuerier(URLDatabase* main_db, URLDatabase* archived_db, bool track_unique)
159      : main_db_(main_db),
160        archived_db_(archived_db),
161        track_unique_(track_unique) {
162  }
163
164  // When we're tracking unique URLs, returns true if this URL has been
165  // previously queried. Only call when tracking unique URLs.
166  bool HasURL(const GURL& url) {
167    DCHECK(track_unique_);
168    return unique_urls_.find(url) != unique_urls_.end();
169  }
170
171  bool GetRowForURL(const GURL& url, URLRow* row) {
172    if (!main_db_->GetRowForURL(url, row)) {
173      if (!archived_db_ || !archived_db_->GetRowForURL(url, row)) {
174        // This row is neither in the main nor the archived DB.
175        return false;
176      }
177    }
178
179    if (track_unique_)
180      unique_urls_.insert(url);
181    return true;
182  }
183
184 private:
185  URLDatabase* main_db_;  // Guaranteed non-NULL.
186  URLDatabase* archived_db_;  // Possibly NULL.
187
188  bool track_unique_;
189
190  // When track_unique_ is set, this is updated with every URL seen so far.
191  std::set<GURL> unique_urls_;
192
193  DISALLOW_COPY_AND_ASSIGN(URLQuerier);
194};
195
196// HistoryBackend --------------------------------------------------------------
197
198HistoryBackend::HistoryBackend(const FilePath& history_dir,
199                               Delegate* delegate,
200                               BookmarkService* bookmark_service)
201    : delegate_(delegate),
202      history_dir_(history_dir),
203      ALLOW_THIS_IN_INITIALIZER_LIST(expirer_(this, bookmark_service)),
204      recent_redirects_(kMaxRedirectCount),
205      backend_destroy_message_loop_(NULL),
206      backend_destroy_task_(NULL),
207      segment_queried_(false),
208      bookmark_service_(bookmark_service) {
209}
210
211HistoryBackend::~HistoryBackend() {
212  DCHECK(!scheduled_commit_) << "Deleting without cleanup";
213  ReleaseDBTasks();
214
215  // First close the databases before optionally running the "destroy" task.
216  if (db_.get()) {
217    // Commit the long-running transaction.
218    db_->CommitTransaction();
219    db_.reset();
220  }
221  if (thumbnail_db_.get()) {
222    thumbnail_db_->CommitTransaction();
223    thumbnail_db_.reset();
224  }
225  if (archived_db_.get()) {
226    archived_db_->CommitTransaction();
227    archived_db_.reset();
228  }
229  if (text_database_.get()) {
230    text_database_->CommitTransaction();
231    text_database_.reset();
232  }
233
234  if (backend_destroy_task_) {
235    // Notify an interested party (typically a unit test) that we're done.
236    DCHECK(backend_destroy_message_loop_);
237    backend_destroy_message_loop_->PostTask(FROM_HERE, backend_destroy_task_);
238  }
239}
240
241void HistoryBackend::Init(bool force_fail) {
242  if (!force_fail)
243    InitImpl();
244  delegate_->DBLoaded();
245}
246
247void HistoryBackend::SetOnBackendDestroyTask(MessageLoop* message_loop,
248                                             Task* task) {
249  if (backend_destroy_task_) {
250    DLOG(WARNING) << "Setting more than one destroy task, overriding";
251    delete backend_destroy_task_;
252  }
253  backend_destroy_message_loop_ = message_loop;
254  backend_destroy_task_ = task;
255}
256
257void HistoryBackend::Closing() {
258  // Any scheduled commit will have a reference to us, we must make it
259  // release that reference before we can be destroyed.
260  CancelScheduledCommit();
261
262  // Release our reference to the delegate, this reference will be keeping the
263  // history service alive.
264  delegate_.reset();
265}
266
267void HistoryBackend::NotifyRenderProcessHostDestruction(const void* host) {
268  tracker_.NotifyRenderProcessHostDestruction(host);
269}
270
271FilePath HistoryBackend::GetThumbnailFileName() const {
272  return history_dir_.Append(chrome::kThumbnailsFilename);
273}
274
275FilePath HistoryBackend::GetFaviconsFileName() const {
276  return history_dir_.Append(chrome::kFaviconsFilename);
277}
278
279FilePath HistoryBackend::GetArchivedFileName() const {
280  return history_dir_.Append(chrome::kArchivedHistoryFilename);
281}
282
283SegmentID HistoryBackend::GetLastSegmentID(VisitID from_visit) {
284  // Set is used to detect referrer loops.  Should not happen, but can
285  // if the database is corrupt.
286  std::set<VisitID> visit_set;
287  VisitID visit_id = from_visit;
288  while (visit_id) {
289    VisitRow row;
290    if (!db_->GetRowForVisit(visit_id, &row))
291      return 0;
292    if (row.segment_id)
293      return row.segment_id;  // Found a visit in this change with a segment.
294
295    // Check the referrer of this visit, if any.
296    visit_id = row.referring_visit;
297
298    if (visit_set.find(visit_id) != visit_set.end()) {
299      NOTREACHED() << "Loop in referer chain, giving up";
300      break;
301    }
302    visit_set.insert(visit_id);
303  }
304  return 0;
305}
306
307SegmentID HistoryBackend::UpdateSegments(const GURL& url,
308                                         VisitID from_visit,
309                                         VisitID visit_id,
310                                         PageTransition::Type transition_type,
311                                         const Time ts) {
312  if (!db_.get())
313    return 0;
314
315  // We only consider main frames.
316  if (!PageTransition::IsMainFrame(transition_type))
317    return 0;
318
319  SegmentID segment_id = 0;
320  PageTransition::Type t = PageTransition::StripQualifier(transition_type);
321
322  // Are we at the beginning of a new segment?
323  if (t == PageTransition::TYPED || t == PageTransition::AUTO_BOOKMARK) {
324    // If so, create or get the segment.
325    std::string segment_name = db_->ComputeSegmentName(url);
326    URLID url_id = db_->GetRowForURL(url, NULL);
327    if (!url_id)
328      return 0;
329
330    if (!(segment_id = db_->GetSegmentNamed(segment_name))) {
331      if (!(segment_id = db_->CreateSegment(url_id, segment_name))) {
332        NOTREACHED();
333        return 0;
334      }
335    } else {
336      // Note: if we update an existing segment, we update the url used to
337      // represent that segment in order to minimize stale most visited
338      // images.
339      db_->UpdateSegmentRepresentationURL(segment_id, url_id);
340    }
341  } else {
342    // Note: it is possible there is no segment ID set for this visit chain.
343    // This can happen if the initial navigation wasn't AUTO_BOOKMARK or
344    // TYPED. (For example GENERATED). In this case this visit doesn't count
345    // toward any segment.
346    if (!(segment_id = GetLastSegmentID(from_visit)))
347      return 0;
348  }
349
350  // Set the segment in the visit.
351  if (!db_->SetSegmentID(visit_id, segment_id)) {
352    NOTREACHED();
353    return 0;
354  }
355
356  // Finally, increase the counter for that segment / day.
357  if (!db_->IncreaseSegmentVisitCount(segment_id, ts, 1)) {
358    NOTREACHED();
359    return 0;
360  }
361  return segment_id;
362}
363
364void HistoryBackend::AddPage(scoped_refptr<HistoryAddPageArgs> request) {
365  DLOG(INFO) << "Adding page " << request->url.possibly_invalid_spec();
366
367  if (!db_.get())
368    return;
369
370  // Will be filled with the URL ID and the visit ID of the last addition.
371  std::pair<URLID, VisitID> last_ids(0, tracker_.GetLastVisit(
372      request->id_scope, request->page_id, request->referrer));
373
374  VisitID from_visit_id = last_ids.second;
375
376  // If a redirect chain is given, we expect the last item in that chain to be
377  // the final URL.
378  DCHECK(request->redirects.size() == 0 ||
379         request->redirects.back() == request->url);
380
381  // Avoid duplicating times in the database, at least as long as pages are
382  // added in order. However, we don't want to disallow pages from recording
383  // times earlier than our last_recorded_time_, because someone might set
384  // their machine's clock back.
385  if (last_requested_time_ == request->time) {
386    last_recorded_time_ = last_recorded_time_ + TimeDelta::FromMicroseconds(1);
387  } else {
388    last_requested_time_ = request->time;
389    last_recorded_time_ = last_requested_time_;
390  }
391
392  // If the user is adding older history, we need to make sure our times
393  // are correct.
394  if (request->time < first_recorded_time_)
395    first_recorded_time_ = request->time;
396
397  PageTransition::Type transition =
398      PageTransition::StripQualifier(request->transition);
399  bool is_keyword_generated = (transition == PageTransition::KEYWORD_GENERATED);
400
401  if (request->redirects.size() <= 1) {
402    // The single entry is both a chain start and end.
403    PageTransition::Type t = request->transition |
404        PageTransition::CHAIN_START | PageTransition::CHAIN_END;
405
406    // No redirect case (one element means just the page itself).
407    last_ids = AddPageVisit(request->url, last_recorded_time_,
408                            last_ids.second, t);
409
410    // Update the segment for this visit. KEYWORD_GENERATED visits should not
411    // result in changing most visited, so we don't update segments (most
412    // visited db).
413    if (!is_keyword_generated) {
414      UpdateSegments(request->url, from_visit_id, last_ids.second, t,
415                     last_recorded_time_);
416    }
417  } else {
418    // Redirect case. Add the redirect chain.
419
420    PageTransition::Type redirect_info = PageTransition::CHAIN_START;
421
422    if (request->redirects[0].SchemeIs(chrome::kAboutScheme)) {
423      // When the redirect source + referrer is "about" we skip it. This
424      // happens when a page opens a new frame/window to about:blank and then
425      // script sets the URL to somewhere else (used to hide the referrer). It
426      // would be nice to keep all these redirects properly but we don't ever
427      // see the initial about:blank load, so we don't know where the
428      // subsequent client redirect came from.
429      //
430      // In this case, we just don't bother hooking up the source of the
431      // redirects, so we remove it.
432      request->redirects.erase(request->redirects.begin());
433    } else if (request->transition & PageTransition::CLIENT_REDIRECT) {
434      redirect_info = PageTransition::CLIENT_REDIRECT;
435      // The first entry in the redirect chain initiated a client redirect.
436      // We don't add this to the database since the referrer is already
437      // there, so we skip over it but change the transition type of the first
438      // transition to client redirect.
439      //
440      // The referrer is invalid when restoring a session that features an
441      // https tab that redirects to a different host or to http. In this
442      // case we don't need to reconnect the new redirect with the existing
443      // chain.
444      if (request->referrer.is_valid()) {
445        DCHECK(request->referrer == request->redirects[0]);
446        request->redirects.erase(request->redirects.begin());
447
448        // If the navigation entry for this visit has replaced that for the
449        // first visit, remove the CHAIN_END marker from the first visit. This
450        // can be called a lot, for example, the page cycler, and most of the
451        // time we won't have changed anything.
452        VisitRow visit_row;
453        if (request->did_replace_entry &&
454            db_->GetRowForVisit(last_ids.second, &visit_row) &&
455            visit_row.transition | PageTransition::CHAIN_END) {
456          visit_row.transition &= ~PageTransition::CHAIN_END;
457          db_->UpdateVisitRow(visit_row);
458        }
459      }
460    }
461
462    for (size_t redirect_index = 0; redirect_index < request->redirects.size();
463         redirect_index++) {
464      PageTransition::Type t = transition | redirect_info;
465
466      // If this is the last transition, add a CHAIN_END marker
467      if (redirect_index == (request->redirects.size() - 1))
468        t = t | PageTransition::CHAIN_END;
469
470      // Record all redirect visits with the same timestamp. We don't display
471      // them anyway, and if we ever decide to, we can reconstruct their order
472      // from the redirect chain.
473      last_ids = AddPageVisit(request->redirects[redirect_index],
474                              last_recorded_time_, last_ids.second, t);
475      if (t & PageTransition::CHAIN_START) {
476        // Update the segment for this visit.
477        UpdateSegments(request->redirects[redirect_index],
478                       from_visit_id, last_ids.second, t, last_recorded_time_);
479      }
480
481      // Subsequent transitions in the redirect list must all be sever
482      // redirects.
483      redirect_info = PageTransition::SERVER_REDIRECT;
484    }
485
486    // Last, save this redirect chain for later so we can set titles & favicons
487    // on the redirected pages properly. It is indexed by the destination page.
488    recent_redirects_.Put(request->url, request->redirects);
489  }
490
491  // TODO(brettw) bug 1140015: Add an "add page" notification so the history
492  // views can keep in sync.
493
494  // Add the last visit to the tracker so we can get outgoing transitions.
495  // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe
496  // navigation anyway, so last_visit_id is always zero for them.  But adding
497  // them here confuses main frame history, so we skip them for now.
498  if (transition != PageTransition::AUTO_SUBFRAME &&
499      transition != PageTransition::MANUAL_SUBFRAME && !is_keyword_generated) {
500    tracker_.AddVisit(request->id_scope, request->page_id, request->url,
501                      last_ids.second);
502  }
503
504  if (text_database_.get()) {
505    text_database_->AddPageURL(request->url, last_ids.first, last_ids.second,
506                               last_recorded_time_);
507  }
508
509  ScheduleCommit();
510}
511
512void HistoryBackend::InitImpl() {
513  DCHECK(!db_.get()) << "Initializing HistoryBackend twice";
514  // In the rare case where the db fails to initialize a dialog may get shown
515  // the blocks the caller, yet allows other messages through. For this reason
516  // we only set db_ to the created database if creation is successful. That
517  // way other methods won't do anything as db_ is still NULL.
518
519  TimeTicks beginning_time = TimeTicks::Now();
520
521  // Compute the file names. Note that the index file can be removed when the
522  // text db manager is finished being hooked up.
523  FilePath history_name = history_dir_.Append(chrome::kHistoryFilename);
524  FilePath thumbnail_name = GetThumbnailFileName();
525  FilePath archived_name = GetArchivedFileName();
526  FilePath tmp_bookmarks_file = history_dir_.Append(
527      chrome::kHistoryBookmarksFileName);
528
529  // History database.
530  db_.reset(new HistoryDatabase());
531  switch (db_->Init(history_name, tmp_bookmarks_file)) {
532    case sql::INIT_OK:
533      break;
534    case sql::INIT_FAILURE:
535      // A NULL db_ will cause all calls on this object to notice this error
536      // and to not continue.
537      delegate_->NotifyProfileError(IDS_COULDNT_OPEN_PROFILE_ERROR);
538      db_.reset();
539      return;
540    case sql::INIT_TOO_NEW:
541      delegate_->NotifyProfileError(IDS_PROFILE_TOO_NEW_ERROR);
542      db_.reset();
543      return;
544    default:
545      NOTREACHED();
546  }
547
548  // Fill the in-memory database and send it back to the history service on the
549  // main thread.
550  InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend;
551  if (mem_backend->Init(history_name))
552    delegate_->SetInMemoryBackend(mem_backend);  // Takes ownership of pointer.
553  else
554    delete mem_backend;  // Error case, run without the in-memory DB.
555  db_->BeginExclusiveMode();  // Must be after the mem backend read the data.
556
557  // Create the history publisher which needs to be passed on to the text and
558  // thumbnail databases for publishing history.
559  history_publisher_.reset(new HistoryPublisher());
560  if (!history_publisher_->Init()) {
561    // The init may fail when there are no indexers wanting our history.
562    // Hence no need to log the failure.
563    history_publisher_.reset();
564  }
565
566  // Full-text database. This has to be first so we can pass it to the
567  // HistoryDatabase for migration.
568  text_database_.reset(new TextDatabaseManager(history_dir_,
569                                               db_.get(), db_.get()));
570  if (!text_database_->Init(history_publisher_.get())) {
571    LOG(WARNING) << "Text database initialization failed, running without it.";
572    text_database_.reset();
573  }
574  if (db_->needs_version_17_migration()) {
575    // See needs_version_17_migration() decl for more. In this case, we want
576    // to erase all the text database files. This must be done after the text
577    // database manager has been initialized, since it knows about all the
578    // files it manages.
579    text_database_->DeleteAll();
580  }
581
582  // Thumbnail database.
583  thumbnail_db_.reset(new ThumbnailDatabase());
584  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTopSites)) {
585    if (!db_->needs_version_18_migration()) {
586      // No convertion needed - use new filename right away.
587      thumbnail_name = GetFaviconsFileName();
588    }
589  }
590  if (thumbnail_db_->Init(thumbnail_name,
591                          history_publisher_.get()) != sql::INIT_OK) {
592    // Unlike the main database, we don't error out when the database is too
593    // new because this error is much less severe. Generally, this shouldn't
594    // happen since the thumbnail and main datbase versions should be in sync.
595    // We'll just continue without thumbnails & favicons in this case or any
596    // other error.
597    LOG(WARNING) << "Could not initialize the thumbnail database.";
598    thumbnail_db_.reset();
599  }
600
601  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTopSites)) {
602    if (db_->needs_version_18_migration()) {
603      LOG(INFO) << "Starting TopSites migration";
604      delegate_->StartTopSitesMigration();
605    }
606  }
607
608  // Archived database.
609  if (db_->needs_version_17_migration()) {
610    // See needs_version_17_migration() decl for more. In this case, we want
611    // to delete the archived database and need to do so before we try to
612    // open the file. We can ignore any error (maybe the file doesn't exist).
613    file_util::Delete(archived_name, false);
614  }
615  archived_db_.reset(new ArchivedDatabase());
616  if (!archived_db_->Init(archived_name)) {
617    LOG(WARNING) << "Could not initialize the archived database.";
618    archived_db_.reset();
619  }
620
621  // Tell the expiration module about all the nice databases we made. This must
622  // happen before db_->Init() is called since the callback ForceArchiveHistory
623  // may need to expire stuff.
624  //
625  // *sigh*, this can all be cleaned up when that migration code is removed.
626  // The main DB initialization should intuitively be first (not that it
627  // actually matters) and the expirer should be set last.
628  expirer_.SetDatabases(db_.get(), archived_db_.get(),
629                        thumbnail_db_.get(), text_database_.get());
630
631  // Open the long-running transaction.
632  db_->BeginTransaction();
633  if (thumbnail_db_.get())
634    thumbnail_db_->BeginTransaction();
635  if (archived_db_.get())
636    archived_db_->BeginTransaction();
637  if (text_database_.get())
638    text_database_->BeginTransaction();
639
640  // Get the first item in our database.
641  db_->GetStartDate(&first_recorded_time_);
642
643  // Start expiring old stuff.
644  expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold));
645
646  HISTOGRAM_TIMES("History.InitTime",
647                  TimeTicks::Now() - beginning_time);
648}
649
650std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
651    const GURL& url,
652    Time time,
653    VisitID referring_visit,
654    PageTransition::Type transition) {
655  // Top-level frame navigations are visible, everything else is hidden
656  bool new_hidden = !PageTransition::IsMainFrame(transition);
657
658  // NOTE: This code must stay in sync with
659  // ExpireHistoryBackend::ExpireURLsForVisits().
660  // TODO(pkasting): http://b/1148304 We shouldn't be marking so many URLs as
661  // typed, which would eliminate the need for this code.
662  int typed_increment = 0;
663  PageTransition::Type transition_type =
664      PageTransition::StripQualifier(transition);
665  if ((transition_type == PageTransition::TYPED &&
666       !PageTransition::IsRedirect(transition)) ||
667      transition_type == PageTransition::KEYWORD_GENERATED)
668    typed_increment = 1;
669
670  // See if this URL is already in the DB.
671  URLRow url_info(url);
672  URLID url_id = db_->GetRowForURL(url, &url_info);
673  if (url_id) {
674    // Update of an existing row.
675    if (PageTransition::StripQualifier(transition) != PageTransition::RELOAD)
676      url_info.set_visit_count(url_info.visit_count() + 1);
677    if (typed_increment)
678      url_info.set_typed_count(url_info.typed_count() + typed_increment);
679    url_info.set_last_visit(time);
680
681    // Only allow un-hiding of pages, never hiding.
682    if (!new_hidden)
683      url_info.set_hidden(false);
684
685    db_->UpdateURLRow(url_id, url_info);
686  } else {
687    // Addition of a new row.
688    url_info.set_visit_count(1);
689    url_info.set_typed_count(typed_increment);
690    url_info.set_last_visit(time);
691    url_info.set_hidden(new_hidden);
692
693    url_id = db_->AddURL(url_info);
694    if (!url_id) {
695      NOTREACHED() << "Adding URL failed.";
696      return std::make_pair(0, 0);
697    }
698    url_info.id_ = url_id;
699
700    // We don't actually add the URL to the full text index at this point. It
701    // might be nice to do this so that even if we get no title or body, the
702    // user can search for URL components and get the page.
703    //
704    // However, in most cases, we'll get at least a title and usually contents,
705    // and this add will be redundant, slowing everything down. As a result,
706    // we ignore this edge case.
707  }
708
709  // Add the visit with the time to the database.
710  VisitRow visit_info(url_id, time, referring_visit, transition, 0);
711  VisitID visit_id = db_->AddVisit(&visit_info);
712
713  if (visit_info.visit_time < first_recorded_time_)
714    first_recorded_time_ = visit_info.visit_time;
715
716  // Broadcast a notification of the visit.
717  if (visit_id) {
718    URLVisitedDetails* details = new URLVisitedDetails;
719    details->transition = transition;
720    details->row = url_info;
721    // TODO(meelapshah) Disabled due to potential PageCycler regression.
722    // Re-enable this.
723    // GetMostRecentRedirectsTo(url, &details->redirects);
724    BroadcastNotifications(NotificationType::HISTORY_URL_VISITED, details);
725  }
726
727  return std::make_pair(url_id, visit_id);
728}
729
730void HistoryBackend::AddPagesWithDetails(const std::vector<URLRow>& urls) {
731  if (!db_.get())
732    return;
733
734  scoped_ptr<URLsModifiedDetails> modified(new URLsModifiedDetails);
735  for (std::vector<URLRow>::const_iterator i = urls.begin();
736       i != urls.end(); ++i) {
737    DCHECK(!i->last_visit().is_null());
738
739    // We will add to either the archived database or the main one depending on
740    // the date of the added visit.
741    URLDatabase* url_database;
742    VisitDatabase* visit_database;
743    if (i->last_visit() < expirer_.GetCurrentArchiveTime()) {
744      if (!archived_db_.get())
745        return;  // No archived database to save it to, just forget this.
746      url_database = archived_db_.get();
747      visit_database = archived_db_.get();
748    } else {
749      url_database = db_.get();
750      visit_database = db_.get();
751    }
752
753    URLRow existing_url;
754    URLID url_id = url_database->GetRowForURL(i->url(), &existing_url);
755    if (!url_id) {
756      // Add the page if it doesn't exist.
757      url_id = url_database->AddURL(*i);
758      if (!url_id) {
759        NOTREACHED() << "Could not add row to DB";
760        return;
761      }
762
763      if (i->typed_count() > 0)
764        modified->changed_urls.push_back(*i);
765    }
766
767    // Add the page to the full text index. This function is also used for
768    // importing. Even though we don't have page contents, we can at least
769    // add the title and URL to the index so they can be searched. We don't
770    // bother to delete any already-existing FTS entries for the URL, since
771    // this is normally called on import.
772    //
773    // If you ever import *after* first run (selecting import from the menu),
774    // then these additional entries will "shadow" the originals when querying
775    // for the most recent match only, and the user won't get snippets. This is
776    // a very minor issue, and fixing it will make import slower, so we don't
777    // bother.
778    bool has_indexed = false;
779    if (text_database_.get()) {
780      // We do not have to make it update the visit database, below, we will
781      // create the visit entry with the indexed flag set.
782      has_indexed = text_database_->AddPageData(i->url(), url_id, 0,
783                                                i->last_visit(),
784                                                i->title(), string16());
785    }
786
787    // Make up a visit to correspond to that page.
788    VisitRow visit_info(url_id, i->last_visit(), 0,
789        PageTransition::LINK | PageTransition::CHAIN_START |
790        PageTransition::CHAIN_END, 0);
791    visit_info.is_indexed = has_indexed;
792    if (!visit_database->AddVisit(&visit_info)) {
793      NOTREACHED() << "Adding visit failed.";
794      return;
795    }
796
797    if (visit_info.visit_time < first_recorded_time_)
798      first_recorded_time_ = visit_info.visit_time;
799  }
800
801  // Broadcast a notification for typed URLs that have been modified. This
802  // will be picked up by the in-memory URL database on the main thread.
803  //
804  // TODO(brettw) bug 1140015: Add an "add page" notification so the history
805  // views can keep in sync.
806  BroadcastNotifications(NotificationType::HISTORY_TYPED_URLS_MODIFIED,
807                         modified.release());
808
809  ScheduleCommit();
810}
811
812void HistoryBackend::SetPageTitle(const GURL& url,
813                                  const string16& title) {
814  if (!db_.get())
815    return;
816
817  // Search for recent redirects which should get the same title. We make a
818  // dummy list containing the exact URL visited if there are no redirects so
819  // the processing below can be the same.
820  history::RedirectList dummy_list;
821  history::RedirectList* redirects;
822  RedirectCache::iterator iter = recent_redirects_.Get(url);
823  if (iter != recent_redirects_.end()) {
824    redirects = &iter->second;
825
826    // This redirect chain should have the destination URL as the last item.
827    DCHECK(!redirects->empty());
828    DCHECK(redirects->back() == url);
829  } else {
830    // No redirect chain stored, make up one containing the URL we want so we
831    // can use the same logic below.
832    dummy_list.push_back(url);
833    redirects = &dummy_list;
834  }
835
836  bool typed_url_changed = false;
837  std::vector<URLRow> changed_urls;
838  for (size_t i = 0; i < redirects->size(); i++) {
839    URLRow row;
840    URLID row_id = db_->GetRowForURL(redirects->at(i), &row);
841    if (row_id && row.title() != title) {
842      row.set_title(title);
843      db_->UpdateURLRow(row_id, row);
844      changed_urls.push_back(row);
845      if (row.typed_count() > 0)
846        typed_url_changed = true;
847    }
848  }
849
850  // Broadcast notifications for typed URLs that have changed. This will
851  // update the in-memory database.
852  //
853  // TODO(brettw) bug 1140020: Broadcast for all changes (not just typed),
854  // in which case some logic can be removed.
855  if (typed_url_changed) {
856    URLsModifiedDetails* modified =
857        new URLsModifiedDetails;
858    for (size_t i = 0; i < changed_urls.size(); i++) {
859      if (changed_urls[i].typed_count() > 0)
860        modified->changed_urls.push_back(changed_urls[i]);
861    }
862    BroadcastNotifications(NotificationType::HISTORY_TYPED_URLS_MODIFIED,
863                           modified);
864  }
865
866  // Update the full text index.
867  if (text_database_.get())
868    text_database_->AddPageTitle(url, title);
869
870  // Only bother committing if things changed.
871  if (!changed_urls.empty())
872    ScheduleCommit();
873}
874
875void HistoryBackend::IterateURLs(HistoryService::URLEnumerator* iterator) {
876  if (db_.get()) {
877    HistoryDatabase::URLEnumerator e;
878    if (db_->InitURLEnumeratorForEverything(&e)) {
879      URLRow info;
880      while (e.GetNextURL(&info)) {
881        iterator->OnURL(info.url());
882      }
883      iterator->OnComplete(true);  // Success.
884      return;
885    }
886  }
887  iterator->OnComplete(false);  // Failure.
888}
889
890bool HistoryBackend::GetAllTypedURLs(std::vector<history::URLRow>* urls) {
891  if (db_.get())
892    return db_->GetAllTypedUrls(urls);
893  return false;
894}
895
896bool HistoryBackend::GetVisitsForURL(URLID id, VisitVector* visits) {
897  if (db_.get())
898    return db_->GetVisitsForURL(id, visits);
899  return false;
900}
901
902bool HistoryBackend::UpdateURL(URLID id, const history::URLRow& url) {
903  if (db_.get())
904    return db_->UpdateURLRow(id, url);
905  return false;
906}
907
908bool HistoryBackend::AddVisits(const GURL& url,
909                               const std::vector<base::Time>& visits) {
910  if (db_.get()) {
911    for (std::vector<base::Time>::const_iterator visit = visits.begin();
912         visit != visits.end(); ++visit) {
913      if (!AddPageVisit(url, *visit, 0, 0).first) {
914        return false;
915      }
916    }
917    ScheduleCommit();
918    return true;
919  }
920  return false;
921}
922
923bool HistoryBackend::RemoveVisits(const VisitVector& visits) {
924  if (db_.get()) {
925    std::map<URLID, int> url_visits_removed;
926    for (VisitVector::const_iterator visit = visits.begin();
927         visit != visits.end(); ++visit) {
928      db_->DeleteVisit(*visit);
929      std::map<URLID, int>::iterator visit_count =
930          url_visits_removed.find(visit->url_id);
931      if (visit_count == url_visits_removed.end()) {
932        url_visits_removed[visit->url_id] = 1;
933      } else {
934        ++visit_count->second;
935      }
936    }
937    for (std::map<URLID, int>::iterator count = url_visits_removed.begin();
938         count != url_visits_removed.end(); ++count) {
939      history::URLRow url_row;
940      if (!db_->GetURLRow(count->first, &url_row)) {
941        return false;
942      }
943      DCHECK(count->second <= url_row.visit_count());
944      url_row.set_visit_count(url_row.visit_count() - count->second);
945      if (!db_->UpdateURLRow(url_row.id(), url_row)) {
946        return false;
947      }
948    }
949    ScheduleCommit();
950    return true;
951  }
952  return false;
953}
954
955bool HistoryBackend::GetURL(const GURL& url, history::URLRow* url_row) {
956  if (db_.get())
957    return db_->GetRowForURL(url, url_row) != 0;
958  return false;
959}
960
961void HistoryBackend::QueryURL(scoped_refptr<QueryURLRequest> request,
962                              const GURL& url,
963                              bool want_visits) {
964  if (request->canceled())
965    return;
966
967  bool success = false;
968  URLRow* row = &request->value.a;
969  VisitVector* visits = &request->value.b;
970  if (db_.get()) {
971    if (db_->GetRowForURL(url, row)) {
972      // Have a row.
973      success = true;
974
975      // Optionally query the visits.
976      if (want_visits)
977        db_->GetVisitsForURL(row->id(), visits);
978    }
979  }
980  request->ForwardResult(QueryURLRequest::TupleType(request->handle(), success,
981                                                    row, visits));
982}
983
984// Segment usage ---------------------------------------------------------------
985
986void HistoryBackend::DeleteOldSegmentData() {
987  if (db_.get())
988    db_->DeleteSegmentData(Time::Now() -
989                           TimeDelta::FromDays(kSegmentDataRetention));
990}
991
992void HistoryBackend::SetSegmentPresentationIndex(SegmentID segment_id,
993                                                 int index) {
994  if (db_.get())
995    db_->SetSegmentPresentationIndex(segment_id, index);
996}
997
998void HistoryBackend::QuerySegmentUsage(
999    scoped_refptr<QuerySegmentUsageRequest> request,
1000    const Time from_time,
1001    int max_result_count) {
1002  if (request->canceled())
1003    return;
1004
1005  if (db_.get()) {
1006    db_->QuerySegmentUsage(from_time, max_result_count, &request->value.get());
1007
1008    // If this is the first time we query segments, invoke
1009    // DeleteOldSegmentData asynchronously. We do this to cleanup old
1010    // entries.
1011    if (!segment_queried_) {
1012      segment_queried_ = true;
1013      MessageLoop::current()->PostTask(FROM_HERE,
1014          NewRunnableMethod(this, &HistoryBackend::DeleteOldSegmentData));
1015    }
1016  }
1017  request->ForwardResult(
1018      QuerySegmentUsageRequest::TupleType(request->handle(),
1019                                          &request->value.get()));
1020}
1021
1022// Keyword visits --------------------------------------------------------------
1023
1024void HistoryBackend::SetKeywordSearchTermsForURL(const GURL& url,
1025                                                 TemplateURL::IDType keyword_id,
1026                                                 const string16& term) {
1027  if (!db_.get())
1028    return;
1029
1030  // Get the ID for this URL.
1031  URLRow url_row;
1032  if (!db_->GetRowForURL(url, &url_row)) {
1033    // There is a small possibility the url was deleted before the keyword
1034    // was added. Ignore the request.
1035    return;
1036  }
1037
1038  db_->SetKeywordSearchTermsForURL(url_row.id(), keyword_id, term);
1039  ScheduleCommit();
1040}
1041
1042void HistoryBackend::DeleteAllSearchTermsForKeyword(
1043    TemplateURL::IDType keyword_id) {
1044  if (!db_.get())
1045    return;
1046
1047  db_->DeleteAllSearchTermsForKeyword(keyword_id);
1048  // TODO(sky): bug 1168470. Need to move from archive dbs too.
1049  ScheduleCommit();
1050}
1051
1052void HistoryBackend::GetMostRecentKeywordSearchTerms(
1053    scoped_refptr<GetMostRecentKeywordSearchTermsRequest> request,
1054    TemplateURL::IDType keyword_id,
1055    const string16& prefix,
1056    int max_count) {
1057  if (request->canceled())
1058    return;
1059
1060  if (db_.get()) {
1061    db_->GetMostRecentKeywordSearchTerms(keyword_id, prefix, max_count,
1062                                         &(request->value));
1063  }
1064  request->ForwardResult(
1065      GetMostRecentKeywordSearchTermsRequest::TupleType(request->handle(),
1066                                                        &request->value));
1067}
1068
1069// Downloads -------------------------------------------------------------------
1070
1071// Get all the download entries from the database.
1072void HistoryBackend::QueryDownloads(
1073    scoped_refptr<DownloadQueryRequest> request) {
1074  if (request->canceled())
1075    return;
1076  if (db_.get())
1077    db_->QueryDownloads(&request->value);
1078  request->ForwardResult(DownloadQueryRequest::TupleType(&request->value));
1079}
1080
1081// Clean up entries that has been corrupted (because of the crash, for example).
1082void HistoryBackend::CleanUpInProgressEntries() {
1083  if (db_.get()) {
1084    // If some "in progress" entries were not updated when Chrome exited, they
1085    // need to be cleaned up.
1086    db_->CleanUpInProgressEntries();
1087  }
1088}
1089
1090// Update a particular download entry.
1091void HistoryBackend::UpdateDownload(int64 received_bytes,
1092                                    int32 state,
1093                                    int64 db_handle) {
1094  if (db_.get())
1095    db_->UpdateDownload(received_bytes, state, db_handle);
1096}
1097
1098// Update the path of a particular download entry.
1099void HistoryBackend::UpdateDownloadPath(const FilePath& path,
1100                                        int64 db_handle) {
1101  if (db_.get())
1102    db_->UpdateDownloadPath(path, db_handle);
1103}
1104
1105// Create a new download entry and pass back the db_handle to it.
1106void HistoryBackend::CreateDownload(
1107    scoped_refptr<DownloadCreateRequest> request,
1108    const DownloadCreateInfo& create_info) {
1109  int64 db_handle = 0;
1110  if (!request->canceled()) {
1111    if (db_.get())
1112      db_handle = db_->CreateDownload(create_info);
1113    request->ForwardResult(DownloadCreateRequest::TupleType(create_info,
1114                                                            db_handle));
1115  }
1116}
1117
1118void HistoryBackend::RemoveDownload(int64 db_handle) {
1119  if (db_.get())
1120    db_->RemoveDownload(db_handle);
1121}
1122
1123void HistoryBackend::RemoveDownloadsBetween(const Time remove_begin,
1124                                            const Time remove_end) {
1125  if (db_.get())
1126    db_->RemoveDownloadsBetween(remove_begin, remove_end);
1127}
1128
1129void HistoryBackend::SearchDownloads(
1130    scoped_refptr<DownloadSearchRequest> request,
1131    const string16& search_text) {
1132  if (request->canceled())
1133    return;
1134  if (db_.get())
1135    db_->SearchDownloads(&request->value, search_text);
1136  request->ForwardResult(DownloadSearchRequest::TupleType(request->handle(),
1137                                                          &request->value));
1138}
1139
1140void HistoryBackend::QueryHistory(scoped_refptr<QueryHistoryRequest> request,
1141                                  const string16& text_query,
1142                                  const QueryOptions& options) {
1143  if (request->canceled())
1144    return;
1145
1146  TimeTicks beginning_time = TimeTicks::Now();
1147
1148  if (db_.get()) {
1149    if (text_query.empty()) {
1150      // Basic history query for the main database.
1151      QueryHistoryBasic(db_.get(), db_.get(), options, &request->value);
1152
1153      // Now query the archived database. This is a bit tricky because we don't
1154      // want to query it if the queried time range isn't going to find anything
1155      // in it.
1156      // TODO(brettw) bug 1171036: do blimpie querying for the archived database
1157      // as well.
1158      // if (archived_db_.get() &&
1159      //     expirer_.GetCurrentArchiveTime() - TimeDelta::FromDays(7)) {
1160    } else {
1161      // Full text history query.
1162      QueryHistoryFTS(text_query, options, &request->value);
1163    }
1164  }
1165
1166  request->ForwardResult(QueryHistoryRequest::TupleType(request->handle(),
1167                                                        &request->value));
1168
1169  UMA_HISTOGRAM_TIMES("History.QueryHistory",
1170                      TimeTicks::Now() - beginning_time);
1171}
1172
1173// Basic time-based querying of history.
1174void HistoryBackend::QueryHistoryBasic(URLDatabase* url_db,
1175                                       VisitDatabase* visit_db,
1176                                       const QueryOptions& options,
1177                                       QueryResults* result) {
1178  // First get all visits.
1179  VisitVector visits;
1180  visit_db->GetVisibleVisitsInRange(options.begin_time, options.end_time,
1181                                    options.max_count, &visits);
1182  DCHECK(options.max_count == 0 ||
1183         static_cast<int>(visits.size()) <= options.max_count);
1184
1185  // Now add them and the URL rows to the results.
1186  URLResult url_result;
1187  for (size_t i = 0; i < visits.size(); i++) {
1188    const VisitRow visit = visits[i];
1189
1190    // Add a result row for this visit, get the URL info from the DB.
1191    if (!url_db->GetURLRow(visit.url_id, &url_result))
1192      continue;  // DB out of sync and URL doesn't exist, try to recover.
1193    if (!url_result.url().is_valid())
1194      continue;  // Don't report invalid URLs in case of corruption.
1195
1196    // The archived database may be out of sync with respect to starring,
1197    // titles, last visit date, etc. Therefore, we query the main DB if the
1198    // current URL database is not the main one.
1199    if (url_db == db_.get()) {
1200      // Currently querying the archived DB, update with the main database to
1201      // catch any interesting stuff. This will update it if it exists in the
1202      // main DB, and do nothing otherwise.
1203      db_->GetRowForURL(url_result.url(), &url_result);
1204    }
1205
1206    url_result.set_visit_time(visit.visit_time);
1207
1208    // We don't set any of the query-specific parts of the URLResult, since
1209    // snippets and stuff don't apply to basic querying.
1210    result->AppendURLBySwapping(&url_result);
1211  }
1212
1213  if (options.begin_time <= first_recorded_time_)
1214    result->set_reached_beginning(true);
1215}
1216
1217void HistoryBackend::QueryHistoryFTS(const string16& text_query,
1218                                     const QueryOptions& options,
1219                                     QueryResults* result) {
1220  if (!text_database_.get())
1221    return;
1222
1223  // Full text query, first get all the FTS results in the time range.
1224  std::vector<TextDatabase::Match> fts_matches;
1225  Time first_time_searched;
1226  text_database_->GetTextMatches(text_query, options,
1227                                 &fts_matches, &first_time_searched);
1228
1229  URLQuerier querier(db_.get(), archived_db_.get(), true);
1230
1231  // Now get the row and visit information for each one.
1232  URLResult url_result;  // Declare outside loop to prevent re-construction.
1233  for (size_t i = 0; i < fts_matches.size(); i++) {
1234    if (options.max_count != 0 &&
1235        static_cast<int>(result->size()) >= options.max_count)
1236      break;  // Got too many items.
1237
1238    // Get the URL, querying the main and archived databases as necessary. If
1239    // this is not found, the history and full text search databases are out
1240    // of sync and we give up with this result.
1241    if (!querier.GetRowForURL(fts_matches[i].url, &url_result))
1242      continue;
1243
1244    if (!url_result.url().is_valid())
1245      continue;  // Don't report invalid URLs in case of corruption.
1246
1247    // Copy over the FTS stuff that the URLDatabase doesn't know about.
1248    // We do this with swap() to avoid copying, since we know we don't
1249    // need the original any more. Note that we override the title with the
1250    // one from FTS, since that will match the title_match_positions (the
1251    // FTS title and the history DB title may differ).
1252    url_result.set_title(fts_matches[i].title);
1253    url_result.title_match_positions_.swap(
1254        fts_matches[i].title_match_positions);
1255    url_result.snippet_.Swap(&fts_matches[i].snippet);
1256
1257    // The visit time also comes from the full text search database. Since it
1258    // has the time, we can avoid an extra query of the visits table.
1259    url_result.set_visit_time(fts_matches[i].time);
1260
1261    // Add it to the vector, this will clear our |url_row| object as a
1262    // result of the swap.
1263    result->AppendURLBySwapping(&url_result);
1264  }
1265
1266  if (options.begin_time <= first_recorded_time_)
1267    result->set_reached_beginning(true);
1268}
1269
1270// Frontend to GetMostRecentRedirectsFrom from the history thread.
1271void HistoryBackend::QueryRedirectsFrom(
1272    scoped_refptr<QueryRedirectsRequest> request,
1273    const GURL& url) {
1274  if (request->canceled())
1275    return;
1276  bool success = GetMostRecentRedirectsFrom(url, &request->value);
1277  request->ForwardResult(QueryRedirectsRequest::TupleType(
1278      request->handle(), url, success, &request->value));
1279}
1280
1281void HistoryBackend::QueryRedirectsTo(
1282    scoped_refptr<QueryRedirectsRequest> request,
1283    const GURL& url) {
1284  if (request->canceled())
1285    return;
1286  bool success = GetMostRecentRedirectsTo(url, &request->value);
1287  request->ForwardResult(QueryRedirectsRequest::TupleType(
1288      request->handle(), url, success, &request->value));
1289}
1290
1291void HistoryBackend::GetVisitCountToHost(
1292    scoped_refptr<GetVisitCountToHostRequest> request,
1293    const GURL& url) {
1294  if (request->canceled())
1295    return;
1296  int count = 0;
1297  Time first_visit;
1298  const bool success = (db_.get() && db_->GetVisitCountToHost(url, &count,
1299                                                              &first_visit));
1300  request->ForwardResult(GetVisitCountToHostRequest::TupleType(
1301      request->handle(), success, count, first_visit));
1302}
1303
1304void HistoryBackend::QueryTopURLsAndRedirects(
1305    scoped_refptr<QueryTopURLsAndRedirectsRequest> request,
1306    int result_count) {
1307  if (request->canceled())
1308    return;
1309
1310  if (!db_.get()) {
1311    request->ForwardResult(QueryTopURLsAndRedirectsRequest::TupleType(
1312        request->handle(), false, NULL, NULL));
1313    return;
1314  }
1315
1316  std::vector<GURL>* top_urls = &request->value.a;
1317  history::RedirectMap* redirects = &request->value.b;
1318
1319  ScopedVector<PageUsageData> data;
1320  db_->QuerySegmentUsage(base::Time::Now() - base::TimeDelta::FromDays(90),
1321      result_count, &data.get());
1322
1323  for (size_t i = 0; i < data.size(); ++i) {
1324    top_urls->push_back(data[i]->GetURL());
1325    RefCountedVector<GURL>* list = new RefCountedVector<GURL>;
1326    GetMostRecentRedirectsFrom(top_urls->back(), &list->data);
1327    (*redirects)[top_urls->back()] = list;
1328  }
1329
1330  request->ForwardResult(QueryTopURLsAndRedirectsRequest::TupleType(
1331      request->handle(), true, top_urls, redirects));
1332}
1333
1334// Will replace QueryTopURLsAndRedirectsRequest.
1335void HistoryBackend::QueryMostVisitedURLs(
1336    scoped_refptr<QueryMostVisitedURLsRequest> request,
1337    int result_count,
1338    int days_back) {
1339  if (request->canceled())
1340    return;
1341
1342  if (!db_.get()) {
1343    // No History Database - return an empty list.
1344    request->ForwardResult(QueryMostVisitedURLsRequest::TupleType(
1345        request->handle(), MostVisitedURLList()));
1346    return;
1347  }
1348
1349  MostVisitedURLList* result = &request->value;
1350
1351  ScopedVector<PageUsageData> data;
1352  db_->QuerySegmentUsage(base::Time::Now() -
1353                         base::TimeDelta::FromDays(days_back),
1354                         result_count, &data.get());
1355
1356  for (size_t i = 0; i < data.size(); ++i) {
1357    PageUsageData* current_data = data[i];
1358    RedirectList redirects;
1359    GetMostRecentRedirectsFrom(current_data->GetURL(), &redirects);
1360    MostVisitedURL url = MakeMostVisitedURL(*current_data, redirects);
1361    result->push_back(url);
1362  }
1363
1364  request->ForwardResult(QueryMostVisitedURLsRequest::TupleType(
1365      request->handle(), *result));
1366}
1367
1368void HistoryBackend::GetRedirectsFromSpecificVisit(
1369    VisitID cur_visit, history::RedirectList* redirects) {
1370  // Follow any redirects from the given visit and add them to the list.
1371  // It *should* be impossible to get a circular chain here, but we check
1372  // just in case to avoid infinite loops.
1373  GURL cur_url;
1374  std::set<VisitID> visit_set;
1375  visit_set.insert(cur_visit);
1376  while (db_->GetRedirectFromVisit(cur_visit, &cur_visit, &cur_url)) {
1377    if (visit_set.find(cur_visit) != visit_set.end()) {
1378      NOTREACHED() << "Loop in visit chain, giving up";
1379      return;
1380    }
1381    visit_set.insert(cur_visit);
1382    redirects->push_back(cur_url);
1383  }
1384}
1385
1386void HistoryBackend::GetRedirectsToSpecificVisit(
1387    VisitID cur_visit,
1388    history::RedirectList* redirects) {
1389  // Follow redirects going to cur_visit. These are added to |redirects| in
1390  // the order they are found. If a redirect chain looks like A -> B -> C and
1391  // |cur_visit| = C, redirects will be {B, A} in that order.
1392  if (!db_.get())
1393    return;
1394
1395  GURL cur_url;
1396  std::set<VisitID> visit_set;
1397  visit_set.insert(cur_visit);
1398  while (db_->GetRedirectToVisit(cur_visit, &cur_visit, &cur_url)) {
1399    if (visit_set.find(cur_visit) != visit_set.end()) {
1400      NOTREACHED() << "Loop in visit chain, giving up";
1401      return;
1402    }
1403    visit_set.insert(cur_visit);
1404    redirects->push_back(cur_url);
1405  }
1406}
1407
1408bool HistoryBackend::GetMostRecentRedirectsFrom(
1409    const GURL& from_url,
1410    history::RedirectList* redirects) {
1411  redirects->clear();
1412  if (!db_.get())
1413    return false;
1414
1415  URLID from_url_id = db_->GetRowForURL(from_url, NULL);
1416  VisitID cur_visit = db_->GetMostRecentVisitForURL(from_url_id, NULL);
1417  if (!cur_visit)
1418    return false;  // No visits for URL.
1419
1420  GetRedirectsFromSpecificVisit(cur_visit, redirects);
1421  return true;
1422}
1423
1424bool HistoryBackend::GetMostRecentRedirectsTo(
1425    const GURL& to_url,
1426    history::RedirectList* redirects) {
1427  redirects->clear();
1428  if (!db_.get())
1429    return false;
1430
1431  URLID to_url_id = db_->GetRowForURL(to_url, NULL);
1432  VisitID cur_visit = db_->GetMostRecentVisitForURL(to_url_id, NULL);
1433  if (!cur_visit)
1434    return false;  // No visits for URL.
1435
1436  GetRedirectsToSpecificVisit(cur_visit, redirects);
1437  return true;
1438}
1439
1440void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider,
1441                                          HistoryURLProviderParams* params) {
1442  // ExecuteWithDB should handle the NULL database case.
1443  provider->ExecuteWithDB(this, db_.get(), params);
1444}
1445
1446void HistoryBackend::SetPageContents(const GURL& url,
1447                                     const string16& contents) {
1448  // This is histogrammed in the text database manager.
1449  if (!text_database_.get())
1450    return;
1451  text_database_->AddPageContents(url, contents);
1452}
1453
1454void HistoryBackend::SetPageThumbnail(
1455    const GURL& url,
1456    const SkBitmap& thumbnail,
1457    const ThumbnailScore& score) {
1458  if (!db_.get() || !thumbnail_db_.get())
1459    return;
1460
1461  URLRow url_row;
1462  URLID url_id = db_->GetRowForURL(url, &url_row);
1463  if (url_id) {
1464    thumbnail_db_->SetPageThumbnail(url, url_id, thumbnail, score,
1465                                    url_row.last_visit());
1466  }
1467
1468  ScheduleCommit();
1469}
1470
1471void HistoryBackend::GetPageThumbnail(
1472    scoped_refptr<GetPageThumbnailRequest> request,
1473    const GURL& page_url) {
1474  if (request->canceled())
1475    return;
1476
1477  scoped_refptr<RefCountedBytes> data;
1478  GetPageThumbnailDirectly(page_url, &data);
1479
1480  request->ForwardResult(GetPageThumbnailRequest::TupleType(
1481      request->handle(), data));
1482}
1483
1484void HistoryBackend::GetPageThumbnailDirectly(
1485    const GURL& page_url,
1486    scoped_refptr<RefCountedBytes>* data) {
1487  if (thumbnail_db_.get()) {
1488    *data = new RefCountedBytes;
1489
1490    // Time the result.
1491    TimeTicks beginning_time = TimeTicks::Now();
1492
1493    history::RedirectList redirects;
1494    URLID url_id;
1495    bool success = false;
1496
1497    // If there are some redirects, try to get a thumbnail from the last
1498    // redirect destination.
1499    if (GetMostRecentRedirectsFrom(page_url, &redirects) &&
1500        !redirects.empty()) {
1501      if ((url_id = db_->GetRowForURL(redirects.back(), NULL)))
1502        success = thumbnail_db_->GetPageThumbnail(url_id, &(*data)->data);
1503    }
1504
1505    // If we don't have a thumbnail from redirects, try the URL directly.
1506    if (!success) {
1507      if ((url_id = db_->GetRowForURL(page_url, NULL)))
1508        success = thumbnail_db_->GetPageThumbnail(url_id, &(*data)->data);
1509    }
1510
1511    // In this rare case, we start to mine the older redirect sessions
1512    // from the visit table to try to find a thumbnail.
1513    if (!success) {
1514      success = GetThumbnailFromOlderRedirect(page_url, &(*data)->data);
1515    }
1516
1517    if (!success)
1518      *data = NULL;  // This will tell the callback there was an error.
1519
1520    UMA_HISTOGRAM_TIMES("History.GetPageThumbnail",
1521                        TimeTicks::Now() - beginning_time);
1522  }
1523}
1524
1525bool HistoryBackend::GetThumbnailFromOlderRedirect(
1526    const GURL& page_url,
1527    std::vector<unsigned char>* data) {
1528  // Look at a few previous visit sessions.
1529  VisitVector older_sessions;
1530  URLID page_url_id = db_->GetRowForURL(page_url, NULL);
1531  static const int kVisitsToSearchForThumbnail = 4;
1532  db_->GetMostRecentVisitsForURL(
1533      page_url_id, kVisitsToSearchForThumbnail, &older_sessions);
1534
1535  // Iterate across all those previous visits, and see if any of the
1536  // final destinations of those redirect chains have a good thumbnail
1537  // for us.
1538  bool success = false;
1539  for (VisitVector::const_iterator it = older_sessions.begin();
1540       !success && it != older_sessions.end(); ++it) {
1541    history::RedirectList redirects;
1542    if (it->visit_id) {
1543      GetRedirectsFromSpecificVisit(it->visit_id, &redirects);
1544
1545      if (!redirects.empty()) {
1546        URLID url_id;
1547        if ((url_id = db_->GetRowForURL(redirects.back(), NULL)))
1548          success = thumbnail_db_->GetPageThumbnail(url_id, data);
1549      }
1550    }
1551  }
1552
1553  return success;
1554}
1555
1556void HistoryBackend::GetFavIcon(scoped_refptr<GetFavIconRequest> request,
1557                                const GURL& icon_url) {
1558  UpdateFavIconMappingAndFetchImpl(NULL, icon_url, request);
1559}
1560
1561void HistoryBackend::UpdateFavIconMappingAndFetch(
1562    scoped_refptr<GetFavIconRequest> request,
1563    const GURL& page_url,
1564    const GURL& icon_url) {
1565  UpdateFavIconMappingAndFetchImpl(&page_url, icon_url, request);
1566}
1567
1568void HistoryBackend::SetFavIconOutOfDateForPage(const GURL& page_url) {
1569  if (!thumbnail_db_.get() || !db_.get())
1570    return;
1571
1572  URLRow url_row;
1573  URLID url_id = db_->GetRowForURL(page_url, &url_row);
1574  if (!url_id || !url_row.favicon_id())
1575    return;
1576
1577  thumbnail_db_->SetFavIconLastUpdateTime(url_row.favicon_id(), Time());
1578  ScheduleCommit();
1579}
1580
1581void HistoryBackend::SetImportedFavicons(
1582    const std::vector<ImportedFavIconUsage>& favicon_usage) {
1583  if (!db_.get() || !thumbnail_db_.get())
1584    return;
1585
1586  Time now = Time::Now();
1587
1588  // Track all URLs that had their favicons set or updated.
1589  std::set<GURL> favicons_changed;
1590
1591  for (size_t i = 0; i < favicon_usage.size(); i++) {
1592    FavIconID favicon_id = thumbnail_db_->GetFavIconIDForFavIconURL(
1593        favicon_usage[i].favicon_url);
1594    if (!favicon_id) {
1595      // This favicon doesn't exist yet, so we create it using the given data.
1596      favicon_id = thumbnail_db_->AddFavIcon(favicon_usage[i].favicon_url);
1597      if (!favicon_id)
1598        continue;  // Unable to add the favicon.
1599      thumbnail_db_->SetFavIcon(favicon_id,
1600          new RefCountedBytes(favicon_usage[i].png_data), now);
1601    }
1602
1603    // Save the mapping from all the URLs to the favicon.
1604    BookmarkService* bookmark_service = GetBookmarkService();
1605    for (std::set<GURL>::const_iterator url = favicon_usage[i].urls.begin();
1606         url != favicon_usage[i].urls.end(); ++url) {
1607      URLRow url_row;
1608      if (!db_->GetRowForURL(*url, &url_row)) {
1609        // If the URL is present as a bookmark, add the url in history to
1610        // save the favicon mapping. This will match with what history db does
1611        // for regular bookmarked URLs with favicons - when history db is
1612        // cleaned, we keep an entry in the db with 0 visits as long as that
1613        // url is bookmarked.
1614        if (bookmark_service && bookmark_service_->IsBookmarked(*url)) {
1615          URLRow url_info(*url);
1616          url_info.set_visit_count(0);
1617          url_info.set_typed_count(0);
1618          url_info.set_last_visit(base::Time());
1619          url_info.set_hidden(false);
1620          url_info.set_favicon_id(favicon_id);
1621          db_->AddURL(url_info);
1622          favicons_changed.insert(*url);
1623        }
1624      } else if (url_row.favicon_id() == 0) {
1625        // URL is present in history, update the favicon *only* if it
1626        // is not set already.
1627        url_row.set_favicon_id(favicon_id);
1628        db_->UpdateURLRow(url_row.id(), url_row);
1629        favicons_changed.insert(*url);
1630      }
1631    }
1632  }
1633
1634  if (!favicons_changed.empty()) {
1635    // Send the notification about the changed favicon URLs.
1636    FavIconChangeDetails* changed_details = new FavIconChangeDetails;
1637    changed_details->urls.swap(favicons_changed);
1638    BroadcastNotifications(NotificationType::FAVICON_CHANGED, changed_details);
1639  }
1640}
1641
1642void HistoryBackend::UpdateFavIconMappingAndFetchImpl(
1643    const GURL* page_url,
1644    const GURL& icon_url,
1645    scoped_refptr<GetFavIconRequest> request) {
1646  if (request->canceled())
1647    return;
1648
1649  bool know_favicon = false;
1650  bool expired = true;
1651  scoped_refptr<RefCountedBytes> data;
1652
1653  if (thumbnail_db_.get()) {
1654    const FavIconID favicon_id =
1655        thumbnail_db_->GetFavIconIDForFavIconURL(icon_url);
1656    if (favicon_id) {
1657      data = new RefCountedBytes;
1658      know_favicon = true;
1659      Time last_updated;
1660      if (thumbnail_db_->GetFavIcon(favicon_id, &last_updated, &data->data,
1661                                    NULL)) {
1662        expired = (Time::Now() - last_updated) >
1663            TimeDelta::FromDays(kFavIconRefetchDays);
1664      }
1665
1666      if (page_url)
1667        SetFavIconMapping(*page_url, favicon_id);
1668    }
1669    // else case, haven't cached entry yet. Caller is responsible for
1670    // downloading the favicon and invoking SetFavIcon.
1671  }
1672  request->ForwardResult(GetFavIconRequest::TupleType(
1673                             request->handle(), know_favicon, data, expired,
1674                             icon_url));
1675}
1676
1677void HistoryBackend::GetFavIconForURL(
1678    scoped_refptr<GetFavIconRequest> request,
1679    const GURL& page_url) {
1680  if (request->canceled())
1681    return;
1682
1683  bool know_favicon = false;
1684  bool expired = false;
1685  GURL icon_url;
1686
1687  scoped_refptr<RefCountedBytes> data;
1688
1689  if (db_.get() && thumbnail_db_.get()) {
1690    // Time the query.
1691    TimeTicks beginning_time = TimeTicks::Now();
1692
1693    URLRow url_info;
1694    data = new RefCountedBytes;
1695    Time last_updated;
1696    if (db_->GetRowForURL(page_url, &url_info) && url_info.favicon_id() &&
1697        thumbnail_db_->GetFavIcon(url_info.favicon_id(), &last_updated,
1698                                  &data->data, &icon_url)) {
1699      know_favicon = true;
1700      expired = (Time::Now() - last_updated) >
1701          TimeDelta::FromDays(kFavIconRefetchDays);
1702    }
1703
1704    UMA_HISTOGRAM_TIMES("History.GetFavIconForURL",
1705                        TimeTicks::Now() - beginning_time);
1706  }
1707
1708  request->ForwardResult(
1709      GetFavIconRequest::TupleType(request->handle(), know_favicon, data,
1710                                   expired, icon_url));
1711}
1712
1713void HistoryBackend::SetFavIcon(
1714    const GURL& page_url,
1715    const GURL& icon_url,
1716    scoped_refptr<RefCountedMemory> data) {
1717  DCHECK(data.get());
1718  if (!thumbnail_db_.get() || !db_.get())
1719    return;
1720
1721  FavIconID id = thumbnail_db_->GetFavIconIDForFavIconURL(icon_url);
1722  if (!id)
1723    id = thumbnail_db_->AddFavIcon(icon_url);
1724
1725  // Set the image data.
1726  thumbnail_db_->SetFavIcon(id, data, Time::Now());
1727
1728  SetFavIconMapping(page_url, id);
1729}
1730
1731void HistoryBackend::SetFavIconMapping(const GURL& page_url,
1732                                       FavIconID id) {
1733  // Find all the pages whose favicons we should set, we want to set it for
1734  // all the pages in the redirect chain if it redirected.
1735  history::RedirectList dummy_list;
1736  history::RedirectList* redirects;
1737  RedirectCache::iterator iter = recent_redirects_.Get(page_url);
1738  if (iter != recent_redirects_.end()) {
1739    redirects = &iter->second;
1740
1741    // This redirect chain should have the destination URL as the last item.
1742    DCHECK(!redirects->empty());
1743    DCHECK(redirects->back() == page_url);
1744  } else {
1745    // No redirect chain stored, make up one containing the URL we want to we
1746    // can use the same logic below.
1747    dummy_list.push_back(page_url);
1748    redirects = &dummy_list;
1749  }
1750
1751  std::set<GURL> favicons_changed;
1752
1753  // Save page <-> favicon association.
1754  for (history::RedirectList::const_iterator i(redirects->begin());
1755       i != redirects->end(); ++i) {
1756    URLRow row;
1757    if (!db_->GetRowForURL(*i, &row) || row.favicon_id() == id)
1758      continue;
1759
1760    FavIconID old_id = row.favicon_id();
1761    if (old_id == id)
1762      continue;
1763    row.set_favicon_id(id);
1764    db_->UpdateURLRow(row.id(), row);
1765
1766    if (old_id) {
1767      // The page's favicon ID changed. This means that the one we just
1768      // changed from could have been orphaned, and we need to re-check it.
1769      // This is not super fast, but this case will get triggered rarely,
1770      // since normally a page will always map to the same favicon ID. It
1771      // will mostly happen for favicons we import.
1772      if (!db_->IsFavIconUsed(old_id) && thumbnail_db_.get())
1773        thumbnail_db_->DeleteFavIcon(old_id);
1774    }
1775
1776    favicons_changed.insert(row.url());
1777  }
1778
1779  // Send the notification about the changed favicons.
1780  FavIconChangeDetails* changed_details = new FavIconChangeDetails;
1781  changed_details->urls.swap(favicons_changed);
1782  BroadcastNotifications(NotificationType::FAVICON_CHANGED, changed_details);
1783
1784  ScheduleCommit();
1785}
1786
1787void HistoryBackend::Commit() {
1788  if (!db_.get())
1789    return;
1790
1791  // Note that a commit may not actually have been scheduled if a caller
1792  // explicitly calls this instead of using ScheduleCommit. Likewise, we
1793  // may reset the flag written by a pending commit. But this is OK! It
1794  // will merely cause extra commits (which is kind of the idea). We
1795  // could optimize more for this case (we may get two extra commits in
1796  // some cases) but it hasn't been important yet.
1797  CancelScheduledCommit();
1798
1799  db_->CommitTransaction();
1800  DCHECK(db_->transaction_nesting() == 0) << "Somebody left a transaction open";
1801  db_->BeginTransaction();
1802
1803  if (thumbnail_db_.get()) {
1804    thumbnail_db_->CommitTransaction();
1805    DCHECK(thumbnail_db_->transaction_nesting() == 0) <<
1806        "Somebody left a transaction open";
1807    thumbnail_db_->BeginTransaction();
1808  }
1809
1810  if (archived_db_.get()) {
1811    archived_db_->CommitTransaction();
1812    archived_db_->BeginTransaction();
1813  }
1814
1815  if (text_database_.get()) {
1816    text_database_->CommitTransaction();
1817    text_database_->BeginTransaction();
1818  }
1819}
1820
1821void HistoryBackend::ScheduleCommit() {
1822  if (scheduled_commit_.get())
1823    return;
1824  scheduled_commit_ = new CommitLaterTask(this);
1825  MessageLoop::current()->PostDelayedTask(FROM_HERE,
1826      NewRunnableMethod(scheduled_commit_.get(),
1827                        &CommitLaterTask::RunCommit),
1828      kCommitIntervalMs);
1829}
1830
1831void HistoryBackend::CancelScheduledCommit() {
1832  if (scheduled_commit_) {
1833    scheduled_commit_->Cancel();
1834    scheduled_commit_ = NULL;
1835  }
1836}
1837
1838void HistoryBackend::ProcessDBTaskImpl() {
1839  if (!db_.get()) {
1840    // db went away, release all the refs.
1841    ReleaseDBTasks();
1842    return;
1843  }
1844
1845  // Remove any canceled tasks.
1846  while (!db_task_requests_.empty() && db_task_requests_.front()->canceled()) {
1847    db_task_requests_.front()->Release();
1848    db_task_requests_.pop_front();
1849  }
1850  if (db_task_requests_.empty())
1851    return;
1852
1853  // Run the first task.
1854  HistoryDBTaskRequest* request = db_task_requests_.front();
1855  db_task_requests_.pop_front();
1856  if (request->value->RunOnDBThread(this, db_.get())) {
1857    // The task is done. Notify the callback.
1858    request->ForwardResult(HistoryDBTaskRequest::TupleType());
1859    // We AddRef'd the request before adding, need to release it now.
1860    request->Release();
1861  } else {
1862    // Tasks wants to run some more. Schedule it at the end of current tasks.
1863    db_task_requests_.push_back(request);
1864    // And process it after an invoke later.
1865    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
1866        this, &HistoryBackend::ProcessDBTaskImpl));
1867  }
1868}
1869
1870void HistoryBackend::ReleaseDBTasks() {
1871  for (std::list<HistoryDBTaskRequest*>::iterator i =
1872       db_task_requests_.begin(); i != db_task_requests_.end(); ++i) {
1873    (*i)->Release();
1874  }
1875  db_task_requests_.clear();
1876}
1877
1878////////////////////////////////////////////////////////////////////////////////
1879//
1880// Generic operations
1881//
1882////////////////////////////////////////////////////////////////////////////////
1883
1884void HistoryBackend::DeleteURLs(const std::vector<GURL>& urls) {
1885  for (std::vector<GURL>::const_iterator url = urls.begin(); url != urls.end();
1886       ++url) {
1887    expirer_.DeleteURL(*url);
1888  }
1889
1890  db_->GetStartDate(&first_recorded_time_);
1891  // Force a commit, if the user is deleting something for privacy reasons, we
1892  // want to get it on disk ASAP.
1893  Commit();
1894}
1895
1896void HistoryBackend::DeleteURL(const GURL& url) {
1897  expirer_.DeleteURL(url);
1898
1899  db_->GetStartDate(&first_recorded_time_);
1900  // Force a commit, if the user is deleting something for privacy reasons, we
1901  // want to get it on disk ASAP.
1902  Commit();
1903}
1904
1905void HistoryBackend::ExpireHistoryBetween(
1906    scoped_refptr<ExpireHistoryRequest> request,
1907    const std::set<GURL>& restrict_urls,
1908    Time begin_time,
1909    Time end_time) {
1910  if (request->canceled())
1911    return;
1912
1913  if (db_.get()) {
1914    if (begin_time.is_null() && end_time.is_null() && restrict_urls.empty()) {
1915      // Special case deleting all history so it can be faster and to reduce the
1916      // possibility of an information leak.
1917      DeleteAllHistory();
1918    } else {
1919      // Clearing parts of history, have the expirer do the depend
1920      expirer_.ExpireHistoryBetween(restrict_urls, begin_time, end_time);
1921
1922      // Force a commit, if the user is deleting something for privacy reasons,
1923      // we want to get it on disk ASAP.
1924      Commit();
1925    }
1926  }
1927
1928  if (begin_time <= first_recorded_time_)
1929    db_->GetStartDate(&first_recorded_time_);
1930
1931  request->ForwardResult(ExpireHistoryRequest::TupleType());
1932
1933  if (history_publisher_.get() && restrict_urls.empty())
1934    history_publisher_->DeleteUserHistoryBetween(begin_time, end_time);
1935}
1936
1937void HistoryBackend::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
1938  if (!db_.get())
1939    return;
1940
1941  for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
1942    URLRow url_row;
1943    if (!db_->GetRowForURL(*i, &url_row))
1944      continue;  // The URL isn't in the db; nothing to do.
1945
1946    VisitVector visits;
1947    db_->GetVisitsForURL(url_row.id(), &visits);
1948
1949    if (visits.empty())
1950      expirer_.DeleteURL(*i);  // There are no more visits; nuke the URL.
1951  }
1952}
1953
1954void HistoryBackend::ProcessDBTask(
1955    scoped_refptr<HistoryDBTaskRequest> request) {
1956  DCHECK(request.get());
1957  if (request->canceled())
1958    return;
1959
1960  bool task_scheduled = !db_task_requests_.empty();
1961  // Make sure we up the refcount of the request. ProcessDBTaskImpl will
1962  // release when done with the task.
1963  request->AddRef();
1964  db_task_requests_.push_back(request.get());
1965  if (!task_scheduled) {
1966    // No other tasks are scheduled. Process request now.
1967    ProcessDBTaskImpl();
1968  }
1969}
1970
1971void HistoryBackend::BroadcastNotifications(
1972    NotificationType type,
1973    HistoryDetails* details_deleted) {
1974  DCHECK(delegate_.get());
1975  delegate_->BroadcastNotifications(type, details_deleted);
1976}
1977
1978// Deleting --------------------------------------------------------------------
1979
1980void HistoryBackend::DeleteAllHistory() {
1981  // Our approach to deleting all history is:
1982  //  1. Copy the bookmarks and their dependencies to new tables with temporary
1983  //     names.
1984  //  2. Delete the original tables. Since tables can not share pages, we know
1985  //     that any data we don't want to keep is now in an unused page.
1986  //  3. Renaming the temporary tables to match the original.
1987  //  4. Vacuuming the database to delete the unused pages.
1988  //
1989  // Since we are likely to have very few bookmarks and their dependencies
1990  // compared to all history, this is also much faster than just deleting from
1991  // the original tables directly.
1992
1993  // Get the bookmarked URLs.
1994  std::vector<GURL> starred_urls;
1995  BookmarkService* bookmark_service = GetBookmarkService();
1996  if (bookmark_service)
1997    bookmark_service_->GetBookmarks(&starred_urls);
1998
1999  std::vector<URLRow> kept_urls;
2000  for (size_t i = 0; i < starred_urls.size(); i++) {
2001    URLRow row;
2002    if (!db_->GetRowForURL(starred_urls[i], &row))
2003      continue;
2004
2005    // Clear the last visit time so when we write these rows they are "clean."
2006    row.set_last_visit(Time());
2007    row.set_visit_count(0);
2008    row.set_typed_count(0);
2009    kept_urls.push_back(row);
2010  }
2011
2012  // Clear thumbnail and favicon history. The favicons for the given URLs will
2013  // be kept.
2014  if (!ClearAllThumbnailHistory(&kept_urls)) {
2015    LOG(ERROR) << "Thumbnail history could not be cleared";
2016    // We continue in this error case. If the user wants to delete their
2017    // history, we should delete as much as we can.
2018  }
2019
2020  // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore,
2021  // we clear the list afterwards to make sure nobody uses this invalid data.
2022  if (!ClearAllMainHistory(kept_urls))
2023    LOG(ERROR) << "Main history could not be cleared";
2024  kept_urls.clear();
2025
2026  // Delete FTS files & archived history.
2027  if (text_database_.get()) {
2028    // We assume that the text database has one transaction on them that we need
2029    // to close & restart (the long-running history transaction).
2030    text_database_->CommitTransaction();
2031    text_database_->DeleteAll();
2032    text_database_->BeginTransaction();
2033  }
2034
2035  if (archived_db_.get()) {
2036    // Close the database and delete the file.
2037    archived_db_.reset();
2038    FilePath archived_file_name = GetArchivedFileName();
2039    file_util::Delete(archived_file_name, false);
2040
2041    // Now re-initialize the database (which may fail).
2042    archived_db_.reset(new ArchivedDatabase());
2043    if (!archived_db_->Init(archived_file_name)) {
2044      LOG(WARNING) << "Could not initialize the archived database.";
2045      archived_db_.reset();
2046    } else {
2047      // Open our long-running transaction on this database.
2048      archived_db_->BeginTransaction();
2049    }
2050  }
2051
2052  db_->GetStartDate(&first_recorded_time_);
2053
2054  // Send out the notfication that history is cleared. The in-memory datdabase
2055  // will pick this up and clear itself.
2056  URLsDeletedDetails* details = new URLsDeletedDetails;
2057  details->all_history = true;
2058  BroadcastNotifications(NotificationType::HISTORY_URLS_DELETED, details);
2059}
2060
2061bool HistoryBackend::ClearAllThumbnailHistory(
2062    std::vector<URLRow>* kept_urls) {
2063  if (!thumbnail_db_.get()) {
2064    // When we have no reference to the thumbnail database, maybe there was an
2065    // error opening it. In this case, we just try to blow it away to try to
2066    // fix the error if it exists. This may fail, in which case either the
2067    // file doesn't exist or there's no more we can do.
2068    file_util::Delete(GetThumbnailFileName(), false);
2069    return true;
2070  }
2071
2072  // Create the duplicate favicon table, this is where the favicons we want
2073  // to keep will be stored.
2074  if (!thumbnail_db_->InitTemporaryFavIconsTable())
2075    return false;
2076
2077  // This maps existing favicon IDs to the ones in the temporary table.
2078  typedef std::map<FavIconID, FavIconID> FavIconMap;
2079  FavIconMap copied_favicons;
2080
2081  // Copy all unique favicons to the temporary table, and update all the
2082  // URLs to have the new IDs.
2083  for (std::vector<URLRow>::iterator i = kept_urls->begin();
2084       i != kept_urls->end(); ++i) {
2085    FavIconID old_id = i->favicon_id();
2086    if (!old_id)
2087      continue;  // URL has no favicon.
2088    FavIconID new_id;
2089
2090    FavIconMap::const_iterator found = copied_favicons.find(old_id);
2091    if (found == copied_favicons.end()) {
2092      new_id = thumbnail_db_->CopyToTemporaryFavIconTable(old_id);
2093      copied_favicons[old_id] = new_id;
2094    } else {
2095      // We already encountered a URL that used this favicon, use the ID we
2096      // previously got.
2097      new_id = found->second;
2098    }
2099    i->set_favicon_id(new_id);
2100  }
2101
2102  // Rename the duplicate favicon table back and recreate the other tables.
2103  // This will make the database consistent again.
2104  thumbnail_db_->CommitTemporaryFavIconTable();
2105  thumbnail_db_->RecreateThumbnailTable();
2106
2107  // Vacuum to remove all the pages associated with the dropped tables. There
2108  // must be no transaction open on the table when we do this. We assume that
2109  // our long-running transaction is open, so we complete it and start it again.
2110  DCHECK(thumbnail_db_->transaction_nesting() == 1);
2111  thumbnail_db_->CommitTransaction();
2112  thumbnail_db_->Vacuum();
2113  thumbnail_db_->BeginTransaction();
2114  return true;
2115}
2116
2117bool HistoryBackend::ClearAllMainHistory(
2118    const std::vector<URLRow>& kept_urls) {
2119  // Create the duplicate URL table. We will copy the kept URLs into this.
2120  if (!db_->CreateTemporaryURLTable())
2121    return false;
2122
2123  // Insert the URLs into the temporary table, we need to keep a map of changed
2124  // IDs since the ID will be different in the new table.
2125  typedef std::map<URLID, URLID> URLIDMap;
2126  URLIDMap old_to_new;  // Maps original ID to new one.
2127  for (std::vector<URLRow>::const_iterator i = kept_urls.begin();
2128       i != kept_urls.end();
2129       ++i) {
2130    URLID new_id = db_->AddTemporaryURL(*i);
2131    old_to_new[i->id()] = new_id;
2132  }
2133
2134  // Replace the original URL table with the temporary one.
2135  if (!db_->CommitTemporaryURLTable())
2136    return false;
2137
2138  // Delete the old tables and recreate them empty.
2139  db_->RecreateAllTablesButURL();
2140
2141  // Vacuum to reclaim the space from the dropped tables. This must be done
2142  // when there is no transaction open, and we assume that our long-running
2143  // transaction is currently open.
2144  db_->CommitTransaction();
2145  db_->Vacuum();
2146  db_->BeginTransaction();
2147  db_->GetStartDate(&first_recorded_time_);
2148
2149  return true;
2150}
2151
2152BookmarkService* HistoryBackend::GetBookmarkService() {
2153  if (bookmark_service_)
2154    bookmark_service_->BlockTillLoaded();
2155  return bookmark_service_;
2156}
2157
2158void HistoryBackend::MigrateThumbnailsDatabase() {
2159  thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(),
2160                                         GetFaviconsFileName());
2161  db_->MigrationToTopSitesDone();
2162}
2163
2164}  // namespace history
2165