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