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