history_backend.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/history/history_backend.h"
6
7#include <algorithm>
8#include <list>
9#include <map>
10#include <set>
11#include <vector>
12
13#include "base/bind.h"
14#include "base/compiler_specific.h"
15#include "base/file_util.h"
16#include "base/memory/scoped_ptr.h"
17#include "base/memory/scoped_vector.h"
18#include "base/message_loop.h"
19#include "base/metrics/histogram.h"
20#include "base/string_util.h"
21#include "base/time.h"
22#include "base/utf_string_conversions.h"
23#include "chrome/browser/api/bookmarks/bookmark_service.h"
24#include "chrome/browser/autocomplete/history_url_provider.h"
25#include "chrome/browser/common/cancelable_request.h"
26#include "chrome/browser/history/history_notifications.h"
27#include "chrome/browser/history/history_publisher.h"
28#include "chrome/browser/history/in_memory_history_backend.h"
29#include "chrome/browser/history/page_usage_data.h"
30#include "chrome/browser/history/select_favicon_frames.h"
31#include "chrome/browser/history/top_sites.h"
32#include "chrome/browser/history/visit_filter.h"
33#include "chrome/common/chrome_constants.h"
34#include "chrome/common/chrome_notification_types.h"
35#include "chrome/common/url_constants.h"
36#include "content/public/browser/download_persistent_store_info.h"
37#include "googleurl/src/gurl.h"
38#include "grit/chromium_strings.h"
39#include "grit/generated_resources.h"
40#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
41#include "sql/error_delegate_util.h"
42
43#if defined(OS_ANDROID)
44#include "chrome/browser/history/android/android_provider_backend.h"
45#endif
46
47using base::Time;
48using base::TimeDelta;
49using base::TimeTicks;
50
51/* The HistoryBackend consists of a number of components:
52
53    HistoryDatabase (stores past 3 months of history)
54      URLDatabase (stores a list of URLs)
55      DownloadDatabase (stores a list of downloads)
56      VisitDatabase (stores a list of visits for the URLs)
57      VisitSegmentDatabase (stores groups of URLs for the most visited view).
58
59    ArchivedDatabase (stores history older than 3 months)
60      URLDatabase (stores a list of URLs)
61      DownloadDatabase (stores a list of downloads)
62      VisitDatabase (stores a list of visits for the URLs)
63
64      (this does not store visit segments as they expire after 3 mos.)
65
66    TextDatabaseManager (manages multiple text database for different times)
67      TextDatabase (represents a single month of full-text index).
68      ...more TextDatabase objects...
69
70    ExpireHistoryBackend (manages moving things from HistoryDatabase to
71                          the ArchivedDatabase and deleting)
72*/
73
74namespace history {
75
76// How long we keep segment data for in days. Currently 3 months.
77// This value needs to be greater or equal to
78// MostVisitedModel::kMostVisitedScope but we don't want to introduce a direct
79// dependency between MostVisitedModel and the history backend.
80static const int kSegmentDataRetention = 90;
81
82// How long we'll wait to do a commit, so that things are batched together.
83static const int kCommitIntervalSeconds = 10;
84
85// The amount of time before we re-fetch the favicon.
86static const int kFaviconRefetchDays = 7;
87
88// GetSessionTabs returns all open tabs, or tabs closed kSessionCloseTimeWindow
89// seconds ago.
90static const int kSessionCloseTimeWindowSecs = 10;
91
92// The maximum number of items we'll allow in the redirect list before
93// deleting some.
94static const int kMaxRedirectCount = 32;
95
96// The number of days old a history entry can be before it is considered "old"
97// and is archived.
98static const int kArchiveDaysThreshold = 90;
99
100// Converts from PageUsageData to MostVisitedURL. |redirects| is a
101// list of redirects for this URL. Empty list means no redirects.
102MostVisitedURL MakeMostVisitedURL(const PageUsageData& page_data,
103                                  const RedirectList& redirects) {
104  MostVisitedURL mv;
105  mv.url = page_data.GetURL();
106  mv.title = page_data.GetTitle();
107  if (redirects.empty()) {
108    // Redirects must contain at least the target url.
109    mv.redirects.push_back(mv.url);
110  } else {
111    mv.redirects = redirects;
112    if (mv.redirects[mv.redirects.size() - 1] != mv.url) {
113      // The last url must be the target url.
114      mv.redirects.push_back(mv.url);
115    }
116  }
117  return mv;
118}
119
120// This task is run on a timer so that commits happen at regular intervals
121// so they are batched together. The important thing about this class is that
122// it supports canceling of the task so the reference to the backend will be
123// freed. The problem is that when history is shutting down, there is likely
124// to be one of these commits still pending and holding a reference.
125//
126// The backend can call Cancel to have this task release the reference. The
127// task will still run (if we ever get to processing the event before
128// shutdown), but it will not do anything.
129//
130// Note that this is a refcounted object and is not a task in itself. It should
131// be assigned to a RunnableMethod.
132//
133// TODO(brettw): bug 1165182: This should be replaced with a
134// base::WeakPtrFactory which will handle everything automatically (like we do
135// in ExpireHistoryBackend).
136class CommitLaterTask : public base::RefCounted<CommitLaterTask> {
137 public:
138  explicit CommitLaterTask(HistoryBackend* history_backend)
139      : history_backend_(history_backend) {
140  }
141
142  // The backend will call this function if it is being destroyed so that we
143  // release our reference.
144  void Cancel() {
145    history_backend_ = NULL;
146  }
147
148  void RunCommit() {
149    if (history_backend_.get())
150      history_backend_->Commit();
151  }
152
153 private:
154  friend class base::RefCounted<CommitLaterTask>;
155
156  ~CommitLaterTask() {}
157
158  scoped_refptr<HistoryBackend> history_backend_;
159};
160
161// Handles querying first the main database, then the full text database if that
162// fails. It will optionally keep track of all URLs seen so duplicates can be
163// eliminated. This is used by the querying sub-functions.
164//
165// TODO(brettw): This class may be able to be simplified or eliminated. After
166// this was written, QueryResults can efficiently look up by URL, so the need
167// for this extra set of previously queried URLs is less important.
168class HistoryBackend::URLQuerier {
169 public:
170  URLQuerier(URLDatabase* main_db, URLDatabase* archived_db, bool track_unique)
171      : main_db_(main_db),
172        archived_db_(archived_db),
173        track_unique_(track_unique) {
174  }
175
176  // When we're tracking unique URLs, returns true if this URL has been
177  // previously queried. Only call when tracking unique URLs.
178  bool HasURL(const GURL& url) {
179    DCHECK(track_unique_);
180    return unique_urls_.find(url) != unique_urls_.end();
181  }
182
183  bool GetRowForURL(const GURL& url, URLRow* row) {
184    if (!main_db_->GetRowForURL(url, row)) {
185      if (!archived_db_ || !archived_db_->GetRowForURL(url, row)) {
186        // This row is neither in the main nor the archived DB.
187        return false;
188      }
189    }
190
191    if (track_unique_)
192      unique_urls_.insert(url);
193    return true;
194  }
195
196 private:
197  URLDatabase* main_db_;  // Guaranteed non-NULL.
198  URLDatabase* archived_db_;  // Possibly NULL.
199
200  bool track_unique_;
201
202  // When track_unique_ is set, this is updated with every URL seen so far.
203  std::set<GURL> unique_urls_;
204
205  DISALLOW_COPY_AND_ASSIGN(URLQuerier);
206};
207
208// KillHistoryDatabaseErrorDelegate -------------------------------------------
209
210class KillHistoryDatabaseErrorDelegate : public sql::ErrorDelegate {
211 public:
212  explicit KillHistoryDatabaseErrorDelegate(HistoryBackend* backend)
213      : backend_(backend),
214        scheduled_killing_database_(false) {
215  }
216
217  // sql::ErrorDelegate implementation.
218  virtual int OnError(int error,
219                      sql::Connection* connection,
220                      sql::Statement* stmt) OVERRIDE {
221    sql::LogAndRecordErrorInHistogram<HistogramUniquifier>(error, connection);
222
223    // Do not schedule killing database more than once. If the first time
224    // failed, it is unlikely that a second time will be successful.
225    if (!scheduled_killing_database_ && sql::IsErrorCatastrophic(error)) {
226      scheduled_killing_database_ = true;
227
228      // Don't just do the close/delete here, as we are being called by |db| and
229      // that seems dangerous.
230      MessageLoop::current()->PostTask(
231          FROM_HERE,
232          base::Bind(&HistoryBackend::KillHistoryDatabase, backend_));
233    }
234
235    return error;
236  }
237
238  // Returns true if the delegate has previously scheduled killing the database.
239  bool scheduled_killing_database() const {
240    return scheduled_killing_database_;
241  }
242
243 private:
244  class HistogramUniquifier {
245   public:
246    static const char* name() { return "Sqlite.History.Error"; }
247  };
248
249  // Do not increment the count on |HistoryBackend| as that would create a
250  // circular reference (HistoryBackend -> HistoryDatabase -> Connection ->
251  // ErrorDelegate -> HistoryBackend).
252  HistoryBackend* backend_;
253
254  // True if the backend has previously scheduled killing the history database.
255  bool scheduled_killing_database_;
256
257  DISALLOW_COPY_AND_ASSIGN(KillHistoryDatabaseErrorDelegate);
258};
259
260// HistoryBackend --------------------------------------------------------------
261
262HistoryBackend::HistoryBackend(const FilePath& history_dir,
263                               int id,
264                               Delegate* delegate,
265                               BookmarkService* bookmark_service)
266    : delegate_(delegate),
267      id_(id),
268      history_dir_(history_dir),
269      ALLOW_THIS_IN_INITIALIZER_LIST(expirer_(this, bookmark_service)),
270      recent_redirects_(kMaxRedirectCount),
271      backend_destroy_message_loop_(NULL),
272      segment_queried_(false),
273      bookmark_service_(bookmark_service) {
274}
275
276HistoryBackend::~HistoryBackend() {
277  DCHECK(!scheduled_commit_) << "Deleting without cleanup";
278  ReleaseDBTasks();
279
280#if defined(OS_ANDROID)
281  // Release AndroidProviderBackend before other objects.
282  android_provider_backend_.reset();
283#endif
284
285  // First close the databases before optionally running the "destroy" task.
286  CloseAllDatabases();
287
288  if (!backend_destroy_task_.is_null()) {
289    // Notify an interested party (typically a unit test) that we're done.
290    DCHECK(backend_destroy_message_loop_);
291    backend_destroy_message_loop_->PostTask(FROM_HERE, backend_destroy_task_);
292  }
293
294#if defined(OS_ANDROID)
295  file_util::Delete(GetAndroidCacheFileName(), false);
296#endif
297}
298
299void HistoryBackend::Init(const std::string& languages, bool force_fail) {
300  if (!force_fail)
301    InitImpl(languages);
302  delegate_->DBLoaded(id_);
303}
304
305void HistoryBackend::SetOnBackendDestroyTask(MessageLoop* message_loop,
306                                             const base::Closure& task) {
307  if (!backend_destroy_task_.is_null())
308    DLOG(WARNING) << "Setting more than one destroy task, overriding";
309  backend_destroy_message_loop_ = message_loop;
310  backend_destroy_task_ = task;
311}
312
313void HistoryBackend::Closing() {
314  // Any scheduled commit will have a reference to us, we must make it
315  // release that reference before we can be destroyed.
316  CancelScheduledCommit();
317
318  // Release our reference to the delegate, this reference will be keeping the
319  // history service alive.
320  delegate_.reset();
321}
322
323void HistoryBackend::NotifyRenderProcessHostDestruction(const void* host) {
324  tracker_.NotifyRenderProcessHostDestruction(host);
325}
326
327FilePath HistoryBackend::GetThumbnailFileName() const {
328  return history_dir_.Append(chrome::kThumbnailsFilename);
329}
330
331FilePath HistoryBackend::GetFaviconsFileName() const {
332  return history_dir_.Append(chrome::kFaviconsFilename);
333}
334
335FilePath HistoryBackend::GetArchivedFileName() const {
336  return history_dir_.Append(chrome::kArchivedHistoryFilename);
337}
338
339#if defined(OS_ANDROID)
340FilePath HistoryBackend::GetAndroidCacheFileName() const {
341  return history_dir_.Append(chrome::kAndroidCacheFilename);
342}
343#endif
344
345SegmentID HistoryBackend::GetLastSegmentID(VisitID from_visit) {
346  // Set is used to detect referrer loops.  Should not happen, but can
347  // if the database is corrupt.
348  std::set<VisitID> visit_set;
349  VisitID visit_id = from_visit;
350  while (visit_id) {
351    VisitRow row;
352    if (!db_->GetRowForVisit(visit_id, &row))
353      return 0;
354    if (row.segment_id)
355      return row.segment_id;  // Found a visit in this change with a segment.
356
357    // Check the referrer of this visit, if any.
358    visit_id = row.referring_visit;
359
360    if (visit_set.find(visit_id) != visit_set.end()) {
361      NOTREACHED() << "Loop in referer chain, giving up";
362      break;
363    }
364    visit_set.insert(visit_id);
365  }
366  return 0;
367}
368
369SegmentID HistoryBackend::UpdateSegments(
370    const GURL& url,
371    VisitID from_visit,
372    VisitID visit_id,
373    content::PageTransition transition_type,
374    const Time ts) {
375  if (!db_.get())
376    return 0;
377
378  // We only consider main frames.
379  if (!content::PageTransitionIsMainFrame(transition_type))
380    return 0;
381
382  SegmentID segment_id = 0;
383  content::PageTransition t =
384      content::PageTransitionStripQualifier(transition_type);
385
386  // Are we at the beginning of a new segment?
387  // Note that navigating to an existing entry (with back/forward) reuses the
388  // same transition type.  We are not adding it as a new segment in that case
389  // because if this was the target of a redirect, we might end up with
390  // 2 entries for the same final URL. Ex: User types google.net, gets
391  // redirected to google.com. A segment is created for google.net. On
392  // google.com users navigates through a link, then press back. That last
393  // navigation is for the entry google.com transition typed. We end up adding
394  // a segment for that one as well. So we end up with google.net and google.com
395  // in the segement table, showing as 2 entries in the NTP.
396  // Note also that we should still be updating the visit count for that segment
397  // which we are not doing now. It should be addressed when
398  // http://crbug.com/96860 is fixed.
399  if ((t == content::PAGE_TRANSITION_TYPED ||
400       t == content::PAGE_TRANSITION_AUTO_BOOKMARK) &&
401      (transition_type & content::PAGE_TRANSITION_FORWARD_BACK) == 0) {
402    // If so, create or get the segment.
403    std::string segment_name = db_->ComputeSegmentName(url);
404    URLID url_id = db_->GetRowForURL(url, NULL);
405    if (!url_id)
406      return 0;
407
408    if (!(segment_id = db_->GetSegmentNamed(segment_name))) {
409      if (!(segment_id = db_->CreateSegment(url_id, segment_name))) {
410        NOTREACHED();
411        return 0;
412      }
413    } else {
414      // Note: if we update an existing segment, we update the url used to
415      // represent that segment in order to minimize stale most visited
416      // images.
417      db_->UpdateSegmentRepresentationURL(segment_id, url_id);
418    }
419  } else {
420    // Note: it is possible there is no segment ID set for this visit chain.
421    // This can happen if the initial navigation wasn't AUTO_BOOKMARK or
422    // TYPED. (For example GENERATED). In this case this visit doesn't count
423    // toward any segment.
424    if (!(segment_id = GetLastSegmentID(from_visit)))
425      return 0;
426  }
427
428  // Set the segment in the visit.
429  if (!db_->SetSegmentID(visit_id, segment_id)) {
430    NOTREACHED();
431    return 0;
432  }
433
434  // Finally, increase the counter for that segment / day.
435  if (!db_->IncreaseSegmentVisitCount(segment_id, ts, 1)) {
436    NOTREACHED();
437    return 0;
438  }
439  return segment_id;
440}
441
442void HistoryBackend::UpdateWithPageEndTime(const void* host,
443                                           int32 page_id,
444                                           const GURL& url,
445                                           Time end_ts) {
446  // Will be filled with the URL ID and the visit ID of the last addition.
447  VisitID visit_id = tracker_.GetLastVisit(host, page_id, url);
448  UpdateVisitDuration(visit_id, end_ts);
449}
450
451void HistoryBackend::UpdateVisitDuration(VisitID visit_id, const Time end_ts) {
452  if (!db_.get())
453    return;
454
455  // Get the starting visit_time for visit_id.
456  VisitRow visit_row;
457  if (db_->GetRowForVisit(visit_id, &visit_row)) {
458    // We should never have a negative duration time even when time is skewed.
459    visit_row.visit_duration = end_ts > visit_row.visit_time ?
460        end_ts - visit_row.visit_time : TimeDelta::FromMicroseconds(0);
461    db_->UpdateVisitRow(visit_row);
462  }
463}
464
465void HistoryBackend::AddPage(const HistoryAddPageArgs& request) {
466  if (!db_.get())
467    return;
468
469  // Will be filled with the URL ID and the visit ID of the last addition.
470  std::pair<URLID, VisitID> last_ids(0, tracker_.GetLastVisit(
471      request.id_scope, request.page_id, request.referrer));
472
473  VisitID from_visit_id = last_ids.second;
474
475  // If a redirect chain is given, we expect the last item in that chain to be
476  // the final URL.
477  DCHECK(request.redirects.empty() ||
478         request.redirects.back() == request.url);
479
480  // If the user is adding older history, we need to make sure our times
481  // are correct.
482  if (request.time < first_recorded_time_)
483    first_recorded_time_ = request.time;
484
485  content::PageTransition request_transition = request.transition;
486  content::PageTransition stripped_transition =
487    content::PageTransitionStripQualifier(request_transition);
488  bool is_keyword_generated =
489      (stripped_transition == content::PAGE_TRANSITION_KEYWORD_GENERATED);
490
491  // If the user is navigating to a not-previously-typed intranet hostname,
492  // change the transition to TYPED so that the omnibox will learn that this is
493  // a known host.
494  bool has_redirects = request.redirects.size() > 1;
495  if (content::PageTransitionIsMainFrame(request_transition) &&
496      (stripped_transition != content::PAGE_TRANSITION_TYPED) &&
497      !is_keyword_generated) {
498    const GURL& origin_url(has_redirects ?
499        request.redirects[0] : request.url);
500    if (origin_url.SchemeIs(chrome::kHttpScheme) ||
501        origin_url.SchemeIs(chrome::kHttpsScheme) ||
502        origin_url.SchemeIs(chrome::kFtpScheme)) {
503      std::string host(origin_url.host());
504      if ((net::RegistryControlledDomainService::GetRegistryLength(
505          host, false) == 0) && !db_->IsTypedHost(host)) {
506        stripped_transition = content::PAGE_TRANSITION_TYPED;
507        request_transition =
508            content::PageTransitionFromInt(
509                stripped_transition |
510                content::PageTransitionGetQualifier(request_transition));
511      }
512    }
513  }
514
515  if (!has_redirects) {
516    // The single entry is both a chain start and end.
517    content::PageTransition t = content::PageTransitionFromInt(
518        request_transition |
519        content::PAGE_TRANSITION_CHAIN_START |
520        content::PAGE_TRANSITION_CHAIN_END);
521
522    // No redirect case (one element means just the page itself).
523    last_ids = AddPageVisit(request.url, request.time,
524                            last_ids.second, t, request.visit_source);
525
526    // Update the segment for this visit. KEYWORD_GENERATED visits should not
527    // result in changing most visited, so we don't update segments (most
528    // visited db).
529    if (!is_keyword_generated) {
530      UpdateSegments(request.url, from_visit_id, last_ids.second, t,
531                     request.time);
532
533      // Update the referrer's duration.
534      UpdateVisitDuration(from_visit_id, request.time);
535    }
536  } else {
537    // Redirect case. Add the redirect chain.
538
539    content::PageTransition redirect_info =
540        content::PAGE_TRANSITION_CHAIN_START;
541
542    RedirectList redirects = request.redirects;
543    if (redirects[0].SchemeIs(chrome::kAboutScheme)) {
544      // When the redirect source + referrer is "about" we skip it. This
545      // happens when a page opens a new frame/window to about:blank and then
546      // script sets the URL to somewhere else (used to hide the referrer). It
547      // would be nice to keep all these redirects properly but we don't ever
548      // see the initial about:blank load, so we don't know where the
549      // subsequent client redirect came from.
550      //
551      // In this case, we just don't bother hooking up the source of the
552      // redirects, so we remove it.
553      redirects.erase(redirects.begin());
554    } else if (request_transition & content::PAGE_TRANSITION_CLIENT_REDIRECT) {
555      redirect_info = content::PAGE_TRANSITION_CLIENT_REDIRECT;
556      // The first entry in the redirect chain initiated a client redirect.
557      // We don't add this to the database since the referrer is already
558      // there, so we skip over it but change the transition type of the first
559      // transition to client redirect.
560      //
561      // The referrer is invalid when restoring a session that features an
562      // https tab that redirects to a different host or to http. In this
563      // case we don't need to reconnect the new redirect with the existing
564      // chain.
565      if (request.referrer.is_valid()) {
566        DCHECK(request.referrer == redirects[0]);
567        redirects.erase(redirects.begin());
568
569        // If the navigation entry for this visit has replaced that for the
570        // first visit, remove the CHAIN_END marker from the first visit. This
571        // can be called a lot, for example, the page cycler, and most of the
572        // time we won't have changed anything.
573        VisitRow visit_row;
574        if (request.did_replace_entry &&
575            db_->GetRowForVisit(last_ids.second, &visit_row) &&
576            visit_row.transition & content::PAGE_TRANSITION_CHAIN_END) {
577          visit_row.transition = content::PageTransitionFromInt(
578              visit_row.transition & ~content::PAGE_TRANSITION_CHAIN_END);
579          db_->UpdateVisitRow(visit_row);
580        }
581      }
582    }
583
584    for (size_t redirect_index = 0; redirect_index < redirects.size();
585         redirect_index++) {
586      content::PageTransition t =
587          content::PageTransitionFromInt(stripped_transition | redirect_info);
588
589      // If this is the last transition, add a CHAIN_END marker
590      if (redirect_index == (redirects.size() - 1)) {
591        t = content::PageTransitionFromInt(
592            t | content::PAGE_TRANSITION_CHAIN_END);
593      }
594
595      // Record all redirect visits with the same timestamp. We don't display
596      // them anyway, and if we ever decide to, we can reconstruct their order
597      // from the redirect chain.
598      last_ids = AddPageVisit(redirects[redirect_index],
599                              request.time, last_ids.second,
600                              t, request.visit_source);
601      if (t & content::PAGE_TRANSITION_CHAIN_START) {
602        // Update the segment for this visit.
603        UpdateSegments(redirects[redirect_index],
604                       from_visit_id, last_ids.second, t, request.time);
605
606        // Update the visit_details for this visit.
607        UpdateVisitDuration(from_visit_id, request.time);
608      }
609
610      // Subsequent transitions in the redirect list must all be server
611      // redirects.
612      redirect_info = content::PAGE_TRANSITION_SERVER_REDIRECT;
613    }
614
615    // Last, save this redirect chain for later so we can set titles & favicons
616    // on the redirected pages properly. It is indexed by the destination page.
617    recent_redirects_.Put(request.url, redirects);
618  }
619
620  // TODO(brettw) bug 1140015: Add an "add page" notification so the history
621  // views can keep in sync.
622
623  // Add the last visit to the tracker so we can get outgoing transitions.
624  // TODO(evanm): Due to http://b/1194536 we lose the referrers of a subframe
625  // navigation anyway, so last_visit_id is always zero for them.  But adding
626  // them here confuses main frame history, so we skip them for now.
627  if (stripped_transition != content::PAGE_TRANSITION_AUTO_SUBFRAME &&
628      stripped_transition != content::PAGE_TRANSITION_MANUAL_SUBFRAME &&
629      !is_keyword_generated) {
630    tracker_.AddVisit(request.id_scope, request.page_id, request.url,
631                      last_ids.second);
632  }
633
634  if (text_database_.get()) {
635    text_database_->AddPageURL(request.url, last_ids.first, last_ids.second,
636                               request.time);
637  }
638
639  ScheduleCommit();
640}
641
642void HistoryBackend::InitImpl(const std::string& languages) {
643  DCHECK(!db_.get()) << "Initializing HistoryBackend twice";
644  // In the rare case where the db fails to initialize a dialog may get shown
645  // the blocks the caller, yet allows other messages through. For this reason
646  // we only set db_ to the created database if creation is successful. That
647  // way other methods won't do anything as db_ is still NULL.
648
649  TimeTicks beginning_time = TimeTicks::Now();
650
651  // Compute the file names. Note that the index file can be removed when the
652  // text db manager is finished being hooked up.
653  FilePath history_name = history_dir_.Append(chrome::kHistoryFilename);
654  FilePath thumbnail_name = GetThumbnailFileName();
655  FilePath archived_name = GetArchivedFileName();
656
657  // History database.
658  db_.reset(new HistoryDatabase());
659
660  // |HistoryDatabase::Init| takes ownership of |error_delegate|.
661  KillHistoryDatabaseErrorDelegate* error_delegate =
662      new KillHistoryDatabaseErrorDelegate(this);
663
664  sql::InitStatus status = db_->Init(history_name, error_delegate);
665  switch (status) {
666    case sql::INIT_OK:
667      break;
668    case sql::INIT_FAILURE: {
669      // A NULL db_ will cause all calls on this object to notice this error
670      // and to not continue. If the error delegate scheduled killing the
671      // database, the task it posted has not executed yet. Try killing the
672      // database now before we close it.
673      bool kill_database = error_delegate->scheduled_killing_database();
674      if (kill_database)
675        KillHistoryDatabase();
676      UMA_HISTOGRAM_BOOLEAN("History.AttemptedToFixProfileError",
677                            kill_database);
678      delegate_->NotifyProfileError(id_, status);
679      db_.reset();
680      return;
681    }
682    default:
683      NOTREACHED();
684  }
685
686  // Fill the in-memory database and send it back to the history service on the
687  // main thread.
688  InMemoryHistoryBackend* mem_backend = new InMemoryHistoryBackend;
689  if (mem_backend->Init(history_name, db_.get()))
690    delegate_->SetInMemoryBackend(id_, mem_backend);  // Takes ownership of
691                                                      // pointer.
692  else
693    delete mem_backend;  // Error case, run without the in-memory DB.
694  db_->BeginExclusiveMode();  // Must be after the mem backend read the data.
695
696  // Create the history publisher which needs to be passed on to the text and
697  // thumbnail databases for publishing history.
698  history_publisher_.reset(new HistoryPublisher());
699  if (!history_publisher_->Init()) {
700    // The init may fail when there are no indexers wanting our history.
701    // Hence no need to log the failure.
702    history_publisher_.reset();
703  }
704
705  // Full-text database. This has to be first so we can pass it to the
706  // HistoryDatabase for migration.
707  text_database_.reset(new TextDatabaseManager(history_dir_,
708                                               db_.get(), db_.get()));
709  if (!text_database_->Init(history_publisher_.get())) {
710    LOG(WARNING) << "Text database initialization failed, running without it.";
711    text_database_.reset();
712  }
713  if (db_->needs_version_17_migration()) {
714    // See needs_version_17_migration() decl for more. In this case, we want
715    // to erase all the text database files. This must be done after the text
716    // database manager has been initialized, since it knows about all the
717    // files it manages.
718    text_database_->DeleteAll();
719  }
720
721  // Thumbnail database.
722  thumbnail_db_.reset(new ThumbnailDatabase());
723  if (!db_->GetNeedsThumbnailMigration()) {
724    // No convertion needed - use new filename right away.
725    thumbnail_name = GetFaviconsFileName();
726  }
727  if (thumbnail_db_->Init(thumbnail_name,
728                          history_publisher_.get(),
729                          db_.get()) != sql::INIT_OK) {
730    // Unlike the main database, we don't error out when the database is too
731    // new because this error is much less severe. Generally, this shouldn't
732    // happen since the thumbnail and main datbase versions should be in sync.
733    // We'll just continue without thumbnails & favicons in this case or any
734    // other error.
735    LOG(WARNING) << "Could not initialize the thumbnail database.";
736    thumbnail_db_.reset();
737  }
738
739  if (db_->GetNeedsThumbnailMigration()) {
740    VLOG(1) << "Starting TopSites migration";
741    delegate_->StartTopSitesMigration(id_);
742  }
743
744  // Archived database.
745  if (db_->needs_version_17_migration()) {
746    // See needs_version_17_migration() decl for more. In this case, we want
747    // to delete the archived database and need to do so before we try to
748    // open the file. We can ignore any error (maybe the file doesn't exist).
749    file_util::Delete(archived_name, false);
750  }
751  archived_db_.reset(new ArchivedDatabase());
752  if (!archived_db_->Init(archived_name)) {
753    LOG(WARNING) << "Could not initialize the archived database.";
754    archived_db_.reset();
755  }
756
757  // Tell the expiration module about all the nice databases we made. This must
758  // happen before db_->Init() is called since the callback ForceArchiveHistory
759  // may need to expire stuff.
760  //
761  // *sigh*, this can all be cleaned up when that migration code is removed.
762  // The main DB initialization should intuitively be first (not that it
763  // actually matters) and the expirer should be set last.
764  expirer_.SetDatabases(db_.get(), archived_db_.get(),
765                        thumbnail_db_.get(), text_database_.get());
766
767  // Open the long-running transaction.
768  db_->BeginTransaction();
769  if (thumbnail_db_.get())
770    thumbnail_db_->BeginTransaction();
771  if (archived_db_.get())
772    archived_db_->BeginTransaction();
773  if (text_database_.get())
774    text_database_->BeginTransaction();
775
776  // Get the first item in our database.
777  db_->GetStartDate(&first_recorded_time_);
778
779  // Start expiring old stuff.
780  expirer_.StartArchivingOldStuff(TimeDelta::FromDays(kArchiveDaysThreshold));
781
782#if defined(OS_ANDROID)
783  if (thumbnail_db_.get()) {
784    android_provider_backend_.reset(new AndroidProviderBackend(
785        GetAndroidCacheFileName(), db_.get(), thumbnail_db_.get(),
786        bookmark_service_, delegate_.get()));
787  }
788#endif
789
790  HISTOGRAM_TIMES("History.InitTime",
791                  TimeTicks::Now() - beginning_time);
792}
793
794void HistoryBackend::CloseAllDatabases() {
795  if (db_.get()) {
796    // Commit the long-running transaction.
797    db_->CommitTransaction();
798    db_.reset();
799  }
800  if (thumbnail_db_.get()) {
801    thumbnail_db_->CommitTransaction();
802    thumbnail_db_.reset();
803  }
804  if (archived_db_.get()) {
805    archived_db_->CommitTransaction();
806    archived_db_.reset();
807  }
808  if (text_database_.get()) {
809    text_database_->CommitTransaction();
810    text_database_.reset();
811  }
812}
813
814std::pair<URLID, VisitID> HistoryBackend::AddPageVisit(
815    const GURL& url,
816    Time time,
817    VisitID referring_visit,
818    content::PageTransition transition,
819    VisitSource visit_source) {
820  // Top-level frame navigations are visible, everything else is hidden
821  bool new_hidden = !content::PageTransitionIsMainFrame(transition);
822
823  // NOTE: This code must stay in sync with
824  // ExpireHistoryBackend::ExpireURLsForVisits().
825  // TODO(pkasting): http://b/1148304 We shouldn't be marking so many URLs as
826  // typed, which would eliminate the need for this code.
827  int typed_increment = 0;
828  content::PageTransition transition_type =
829      content::PageTransitionStripQualifier(transition);
830  if ((transition_type == content::PAGE_TRANSITION_TYPED &&
831      !content::PageTransitionIsRedirect(transition)) ||
832      transition_type == content::PAGE_TRANSITION_KEYWORD_GENERATED)
833    typed_increment = 1;
834
835  // See if this URL is already in the DB.
836  URLRow url_info(url);
837  URLID url_id = db_->GetRowForURL(url, &url_info);
838  if (url_id) {
839    // Update of an existing row.
840    if (content::PageTransitionStripQualifier(transition) !=
841        content::PAGE_TRANSITION_RELOAD)
842      url_info.set_visit_count(url_info.visit_count() + 1);
843    if (typed_increment)
844      url_info.set_typed_count(url_info.typed_count() + typed_increment);
845    url_info.set_last_visit(time);
846
847    // Only allow un-hiding of pages, never hiding.
848    if (!new_hidden)
849      url_info.set_hidden(false);
850
851    db_->UpdateURLRow(url_id, url_info);
852  } else {
853    // Addition of a new row.
854    url_info.set_visit_count(1);
855    url_info.set_typed_count(typed_increment);
856    url_info.set_last_visit(time);
857    url_info.set_hidden(new_hidden);
858
859    url_id = db_->AddURL(url_info);
860    if (!url_id) {
861      NOTREACHED() << "Adding URL failed.";
862      return std::make_pair(0, 0);
863    }
864    url_info.id_ = url_id;
865
866    // We don't actually add the URL to the full text index at this point. It
867    // might be nice to do this so that even if we get no title or body, the
868    // user can search for URL components and get the page.
869    //
870    // However, in most cases, we'll get at least a title and usually contents,
871    // and this add will be redundant, slowing everything down. As a result,
872    // we ignore this edge case.
873  }
874
875  // Add the visit with the time to the database.
876  VisitRow visit_info(url_id, time, referring_visit, transition, 0);
877  VisitID visit_id = db_->AddVisit(&visit_info, visit_source);
878  NotifyVisitObservers(visit_info);
879
880  if (visit_info.visit_time < first_recorded_time_)
881    first_recorded_time_ = visit_info.visit_time;
882
883  // Broadcast a notification of the visit.
884  if (visit_id) {
885    URLVisitedDetails* details = new URLVisitedDetails;
886    details->transition = transition;
887    details->row = url_info;
888    // TODO(meelapshah) Disabled due to potential PageCycler regression.
889    // Re-enable this.
890    // GetMostRecentRedirectsTo(url, &details->redirects);
891    BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URL_VISITED, details);
892  } else {
893    VLOG(0) << "Failed to build visit insert statement:  "
894            << "url_id = " << url_id;
895  }
896
897  return std::make_pair(url_id, visit_id);
898}
899
900void HistoryBackend::AddPagesWithDetails(const URLRows& urls,
901                                         VisitSource visit_source) {
902  if (!db_.get())
903    return;
904
905  scoped_ptr<URLsModifiedDetails> modified(new URLsModifiedDetails);
906  for (URLRows::const_iterator i = urls.begin(); i != urls.end(); ++i) {
907    DCHECK(!i->last_visit().is_null());
908
909    // We will add to either the archived database or the main one depending on
910    // the date of the added visit.
911    URLDatabase* url_database;
912    VisitDatabase* visit_database;
913    if (IsExpiredVisitTime(i->last_visit())) {
914      if (!archived_db_.get())
915        return;  // No archived database to save it to, just forget this.
916      url_database = archived_db_.get();
917      visit_database = archived_db_.get();
918    } else {
919      url_database = db_.get();
920      visit_database = db_.get();
921    }
922
923    URLRow existing_url;
924    URLID url_id = url_database->GetRowForURL(i->url(), &existing_url);
925    if (!url_id) {
926      // Add the page if it doesn't exist.
927      url_id = url_database->AddURL(*i);
928      if (!url_id) {
929        NOTREACHED() << "Could not add row to DB";
930        return;
931      }
932
933      if (i->typed_count() > 0) {
934        modified->changed_urls.push_back(*i);
935        modified->changed_urls.back().set_id(url_id);  // *i likely has |id_| 0.
936      }
937    }
938
939    // Add the page to the full text index. This function is also used for
940    // importing. Even though we don't have page contents, we can at least
941    // add the title and URL to the index so they can be searched. We don't
942    // bother to delete any already-existing FTS entries for the URL, since
943    // this is normally called on import.
944    //
945    // If you ever import *after* first run (selecting import from the menu),
946    // then these additional entries will "shadow" the originals when querying
947    // for the most recent match only, and the user won't get snippets. This is
948    // a very minor issue, and fixing it will make import slower, so we don't
949    // bother.
950    bool has_indexed = false;
951    if (text_database_.get()) {
952      // We do not have to make it update the visit database, below, we will
953      // create the visit entry with the indexed flag set.
954      has_indexed = text_database_->AddPageData(i->url(), url_id, 0,
955                                                i->last_visit(),
956                                                i->title(), string16());
957    }
958
959    // Sync code manages the visits itself.
960    if (visit_source != SOURCE_SYNCED) {
961      // Make up a visit to correspond to the last visit to the page.
962      VisitRow visit_info(url_id, i->last_visit(), 0,
963                          content::PageTransitionFromInt(
964                              content::PAGE_TRANSITION_LINK |
965                              content::PAGE_TRANSITION_CHAIN_START |
966                              content::PAGE_TRANSITION_CHAIN_END), 0);
967      visit_info.is_indexed = has_indexed;
968      if (!visit_database->AddVisit(&visit_info, visit_source)) {
969        NOTREACHED() << "Adding visit failed.";
970        return;
971      }
972      NotifyVisitObservers(visit_info);
973
974      if (visit_info.visit_time < first_recorded_time_)
975        first_recorded_time_ = visit_info.visit_time;
976    }
977  }
978
979  // Broadcast a notification for typed URLs that have been modified. This
980  // will be picked up by the in-memory URL database on the main thread.
981  //
982  // TODO(brettw) bug 1140015: Add an "add page" notification so the history
983  // views can keep in sync.
984  BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
985                         modified.release());
986
987  ScheduleCommit();
988}
989
990bool HistoryBackend::IsExpiredVisitTime(const base::Time& time) {
991  return time < expirer_.GetCurrentArchiveTime();
992}
993
994void HistoryBackend::SetPageTitle(const GURL& url,
995                                  const string16& title) {
996  if (!db_.get())
997    return;
998
999  // Update the full text index.
1000  if (text_database_.get())
1001    text_database_->AddPageTitle(url, title);
1002
1003  // Search for recent redirects which should get the same title. We make a
1004  // dummy list containing the exact URL visited if there are no redirects so
1005  // the processing below can be the same.
1006  history::RedirectList dummy_list;
1007  history::RedirectList* redirects;
1008  RedirectCache::iterator iter = recent_redirects_.Get(url);
1009  if (iter != recent_redirects_.end()) {
1010    redirects = &iter->second;
1011
1012    // This redirect chain should have the destination URL as the last item.
1013    DCHECK(!redirects->empty());
1014    DCHECK(redirects->back() == url);
1015  } else {
1016    // No redirect chain stored, make up one containing the URL we want so we
1017    // can use the same logic below.
1018    dummy_list.push_back(url);
1019    redirects = &dummy_list;
1020  }
1021
1022  scoped_ptr<URLsModifiedDetails> details(new URLsModifiedDetails);
1023  for (size_t i = 0; i < redirects->size(); i++) {
1024    URLRow row;
1025    URLID row_id = db_->GetRowForURL(redirects->at(i), &row);
1026    if (row_id && row.title() != title) {
1027      row.set_title(title);
1028      db_->UpdateURLRow(row_id, row);
1029      details->changed_urls.push_back(row);
1030    }
1031  }
1032
1033  // Broadcast notifications for any URLs that have changed. This will
1034  // update the in-memory database and the InMemoryURLIndex.
1035  if (!details->changed_urls.empty()) {
1036    BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1037                           details.release());
1038    ScheduleCommit();
1039  }
1040}
1041
1042void HistoryBackend::AddPageNoVisitForBookmark(const GURL& url,
1043                                               const string16& title) {
1044  if (!db_.get())
1045    return;
1046
1047  URLRow url_info(url);
1048  URLID url_id = db_->GetRowForURL(url, &url_info);
1049  if (url_id) {
1050    // URL is already known, nothing to do.
1051    return;
1052  }
1053
1054  if (!title.empty()) {
1055    url_info.set_title(title);
1056  } else {
1057    url_info.set_title(UTF8ToUTF16(url.spec()));
1058  }
1059
1060  url_info.set_last_visit(Time::Now());
1061  // Mark the page hidden. If the user types it in, it'll unhide.
1062  url_info.set_hidden(true);
1063
1064  db_->AddURL(url_info);
1065}
1066
1067void HistoryBackend::IterateURLs(HistoryService::URLEnumerator* iterator) {
1068  if (db_.get()) {
1069    HistoryDatabase::URLEnumerator e;
1070    if (db_->InitURLEnumeratorForEverything(&e)) {
1071      URLRow info;
1072      while (e.GetNextURL(&info)) {
1073        iterator->OnURL(info);
1074      }
1075      iterator->OnComplete(true);  // Success.
1076      return;
1077    }
1078  }
1079  iterator->OnComplete(false);  // Failure.
1080}
1081
1082bool HistoryBackend::GetAllTypedURLs(URLRows* urls) {
1083  if (db_.get())
1084    return db_->GetAllTypedUrls(urls);
1085  return false;
1086}
1087
1088bool HistoryBackend::GetVisitsForURL(URLID id, VisitVector* visits) {
1089  if (db_.get())
1090    return db_->GetVisitsForURL(id, visits);
1091  return false;
1092}
1093
1094bool HistoryBackend::GetMostRecentVisitsForURL(URLID id,
1095                                               int max_visits,
1096                                               VisitVector* visits) {
1097  if (db_.get())
1098    return db_->GetMostRecentVisitsForURL(id, max_visits, visits);
1099  return false;
1100}
1101
1102bool HistoryBackend::UpdateURL(URLID id, const history::URLRow& url) {
1103  if (db_.get())
1104    return db_->UpdateURLRow(id, url);
1105  return false;
1106}
1107
1108bool HistoryBackend::AddVisits(const GURL& url,
1109                               const std::vector<VisitInfo>& visits,
1110                               VisitSource visit_source) {
1111  if (db_.get()) {
1112    for (std::vector<VisitInfo>::const_iterator visit = visits.begin();
1113         visit != visits.end(); ++visit) {
1114      if (!AddPageVisit(
1115              url, visit->first, 0, visit->second, visit_source).first) {
1116        return false;
1117      }
1118    }
1119    ScheduleCommit();
1120    return true;
1121  }
1122  return false;
1123}
1124
1125bool HistoryBackend::RemoveVisits(const VisitVector& visits) {
1126  if (!db_.get())
1127    return false;
1128
1129  expirer_.ExpireVisits(visits);
1130  ScheduleCommit();
1131  return true;
1132}
1133
1134bool HistoryBackend::GetVisitsSource(const VisitVector& visits,
1135                                     VisitSourceMap* sources) {
1136  if (!db_.get())
1137    return false;
1138
1139  db_->GetVisitsSource(visits, sources);
1140  return true;
1141}
1142
1143bool HistoryBackend::GetURL(const GURL& url, history::URLRow* url_row) {
1144  if (db_.get())
1145    return db_->GetRowForURL(url, url_row) != 0;
1146  return false;
1147}
1148
1149void HistoryBackend::QueryURL(scoped_refptr<QueryURLRequest> request,
1150                              const GURL& url,
1151                              bool want_visits) {
1152  if (request->canceled())
1153    return;
1154
1155  bool success = false;
1156  URLRow* row = &request->value.a;
1157  VisitVector* visits = &request->value.b;
1158  if (db_.get()) {
1159    if (db_->GetRowForURL(url, row)) {
1160      // Have a row.
1161      success = true;
1162
1163      // Optionally query the visits.
1164      if (want_visits)
1165        db_->GetVisitsForURL(row->id(), visits);
1166    }
1167  }
1168  request->ForwardResult(request->handle(), success, row, visits);
1169}
1170
1171// Segment usage ---------------------------------------------------------------
1172
1173void HistoryBackend::DeleteOldSegmentData() {
1174  if (db_.get())
1175    db_->DeleteSegmentData(Time::Now() -
1176                           TimeDelta::FromDays(kSegmentDataRetention));
1177}
1178
1179void HistoryBackend::SetSegmentPresentationIndex(SegmentID segment_id,
1180                                                 int index) {
1181  if (db_.get())
1182    db_->SetSegmentPresentationIndex(segment_id, index);
1183}
1184
1185void HistoryBackend::QuerySegmentUsage(
1186    scoped_refptr<QuerySegmentUsageRequest> request,
1187    const Time from_time,
1188    int max_result_count) {
1189  if (request->canceled())
1190    return;
1191
1192  if (db_.get()) {
1193    db_->QuerySegmentUsage(from_time, max_result_count, &request->value.get());
1194
1195    // If this is the first time we query segments, invoke
1196    // DeleteOldSegmentData asynchronously. We do this to cleanup old
1197    // entries.
1198    if (!segment_queried_) {
1199      segment_queried_ = true;
1200      MessageLoop::current()->PostTask(
1201          FROM_HERE,
1202          base::Bind(&HistoryBackend::DeleteOldSegmentData, this));
1203    }
1204  }
1205  request->ForwardResult(request->handle(), &request->value.get());
1206}
1207
1208// Keyword visits --------------------------------------------------------------
1209
1210void HistoryBackend::SetKeywordSearchTermsForURL(const GURL& url,
1211                                                 TemplateURLID keyword_id,
1212                                                 const string16& term) {
1213  if (!db_.get())
1214    return;
1215
1216  // Get the ID for this URL.
1217  URLRow url_row;
1218  if (!db_->GetRowForURL(url, &url_row)) {
1219    // There is a small possibility the url was deleted before the keyword
1220    // was added. Ignore the request.
1221    return;
1222  }
1223
1224  db_->SetKeywordSearchTermsForURL(url_row.id(), keyword_id, term);
1225
1226  // details is deleted by BroadcastNotifications.
1227  KeywordSearchTermDetails* details = new KeywordSearchTermDetails;
1228  details->url = url;
1229  details->keyword_id = keyword_id;
1230  details->term = term;
1231  BroadcastNotifications(
1232      chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, details);
1233  ScheduleCommit();
1234}
1235
1236void HistoryBackend::DeleteAllSearchTermsForKeyword(
1237    TemplateURLID keyword_id) {
1238  if (!db_.get())
1239    return;
1240
1241  db_->DeleteAllSearchTermsForKeyword(keyword_id);
1242  // TODO(sky): bug 1168470. Need to move from archive dbs too.
1243  ScheduleCommit();
1244}
1245
1246void HistoryBackend::GetMostRecentKeywordSearchTerms(
1247    scoped_refptr<GetMostRecentKeywordSearchTermsRequest> request,
1248    TemplateURLID keyword_id,
1249    const string16& prefix,
1250    int max_count) {
1251  if (request->canceled())
1252    return;
1253
1254  if (db_.get()) {
1255    db_->GetMostRecentKeywordSearchTerms(keyword_id, prefix, max_count,
1256                                         &(request->value));
1257  }
1258  request->ForwardResult(request->handle(), &request->value);
1259}
1260
1261// Downloads -------------------------------------------------------------------
1262
1263void HistoryBackend::GetNextDownloadId(
1264    scoped_refptr<DownloadNextIdRequest> request) {
1265  if (request->canceled()) return;
1266  if (db_.get()) {
1267    request->value = db_->next_download_id();
1268  } else {
1269    request->value = 0;
1270  }
1271  request->ForwardResult(request->value);
1272}
1273
1274// Get all the download entries from the database.
1275void HistoryBackend::QueryDownloads(
1276    scoped_refptr<DownloadQueryRequest> request) {
1277  if (request->canceled())
1278    return;
1279  if (db_.get())
1280    db_->QueryDownloads(&request->value);
1281  request->ForwardResult(&request->value);
1282}
1283
1284// Clean up entries that has been corrupted (because of the crash, for example).
1285void HistoryBackend::CleanUpInProgressEntries() {
1286  if (db_.get()) {
1287    // If some "in progress" entries were not updated when Chrome exited, they
1288    // need to be cleaned up.
1289    db_->CleanUpInProgressEntries();
1290  }
1291}
1292
1293// Update a particular download entry.
1294void HistoryBackend::UpdateDownload(
1295    const content::DownloadPersistentStoreInfo& data) {
1296  if (db_.get())
1297    db_->UpdateDownload(data);
1298}
1299
1300// Update the path of a particular download entry.
1301void HistoryBackend::UpdateDownloadPath(const FilePath& path,
1302                                        int64 db_handle) {
1303  if (db_.get())
1304    db_->UpdateDownloadPath(path, db_handle);
1305}
1306
1307// Create a new download entry and pass back the db_handle to it.
1308void HistoryBackend::CreateDownload(
1309    scoped_refptr<DownloadCreateRequest> request,
1310    int32 id,
1311    const content::DownloadPersistentStoreInfo& history_info) {
1312  int64 db_handle = 0;
1313  if (!request->canceled()) {
1314    if (db_.get())
1315      db_handle = db_->CreateDownload(history_info);
1316    request->ForwardResult(id, db_handle);
1317  }
1318}
1319
1320void HistoryBackend::RemoveDownload(int64 db_handle) {
1321  if (db_.get())
1322    db_->RemoveDownload(db_handle);
1323}
1324
1325void HistoryBackend::RemoveDownloadsBetween(const Time remove_begin,
1326                                            const Time remove_end) {
1327  if (db_.get())
1328    db_->RemoveDownloadsBetween(remove_begin, remove_end);
1329}
1330
1331void HistoryBackend::QueryHistory(scoped_refptr<QueryHistoryRequest> request,
1332                                  const string16& text_query,
1333                                  const QueryOptions& options) {
1334  if (request->canceled())
1335    return;
1336
1337  TimeTicks beginning_time = TimeTicks::Now();
1338
1339  if (db_.get()) {
1340    if (text_query.empty()) {
1341      // Basic history query for the main database.
1342      QueryHistoryBasic(db_.get(), db_.get(), options, &request->value);
1343
1344      // Now query the archived database. This is a bit tricky because we don't
1345      // want to query it if the queried time range isn't going to find anything
1346      // in it.
1347      // TODO(brettw) bug 1171036: do blimpie querying for the archived database
1348      // as well.
1349      // if (archived_db_.get() &&
1350      //     expirer_.GetCurrentArchiveTime() - TimeDelta::FromDays(7)) {
1351    } else {
1352      // Full text history query.
1353      QueryHistoryFTS(text_query, options, &request->value);
1354    }
1355  }
1356
1357  request->ForwardResult(request->handle(), &request->value);
1358
1359  UMA_HISTOGRAM_TIMES("History.QueryHistory",
1360                      TimeTicks::Now() - beginning_time);
1361}
1362
1363// Basic time-based querying of history.
1364void HistoryBackend::QueryHistoryBasic(URLDatabase* url_db,
1365                                       VisitDatabase* visit_db,
1366                                       const QueryOptions& options,
1367                                       QueryResults* result) {
1368  // First get all visits.
1369  VisitVector visits;
1370  visit_db->GetVisibleVisitsInRange(options.begin_time, options.end_time,
1371                                    options.max_count, &visits);
1372  DCHECK(options.max_count == 0 ||
1373         static_cast<int>(visits.size()) <= options.max_count);
1374
1375  // Now add them and the URL rows to the results.
1376  URLResult url_result;
1377  for (size_t i = 0; i < visits.size(); i++) {
1378    const VisitRow visit = visits[i];
1379
1380    // Add a result row for this visit, get the URL info from the DB.
1381    if (!url_db->GetURLRow(visit.url_id, &url_result)) {
1382      VLOG(0) << "Failed to get id " << visit.url_id
1383              << " from history.urls.";
1384      continue;  // DB out of sync and URL doesn't exist, try to recover.
1385    }
1386
1387    if (!url_result.url().is_valid()) {
1388      VLOG(0) << "Got invalid URL from history.urls with id "
1389              << visit.url_id << ":  "
1390              << url_result.url().possibly_invalid_spec();
1391      continue;  // Don't report invalid URLs in case of corruption.
1392    }
1393
1394    // The archived database may be out of sync with respect to starring,
1395    // titles, last visit date, etc. Therefore, we query the main DB if the
1396    // current URL database is not the main one.
1397    if (url_db == db_.get()) {
1398      // Currently querying the archived DB, update with the main database to
1399      // catch any interesting stuff. This will update it if it exists in the
1400      // main DB, and do nothing otherwise.
1401      db_->GetRowForURL(url_result.url(), &url_result);
1402    }
1403
1404    url_result.set_visit_time(visit.visit_time);
1405
1406    // We don't set any of the query-specific parts of the URLResult, since
1407    // snippets and stuff don't apply to basic querying.
1408    result->AppendURLBySwapping(&url_result);
1409  }
1410
1411  if (options.begin_time <= first_recorded_time_)
1412    result->set_reached_beginning(true);
1413}
1414
1415void HistoryBackend::QueryHistoryFTS(const string16& text_query,
1416                                     const QueryOptions& options,
1417                                     QueryResults* result) {
1418  if (!text_database_.get())
1419    return;
1420
1421  // Full text query, first get all the FTS results in the time range.
1422  std::vector<TextDatabase::Match> fts_matches;
1423  Time first_time_searched;
1424  text_database_->GetTextMatches(text_query, options,
1425                                 &fts_matches, &first_time_searched);
1426
1427  URLQuerier querier(db_.get(), archived_db_.get(), true);
1428
1429  // Now get the row and visit information for each one.
1430  URLResult url_result;  // Declare outside loop to prevent re-construction.
1431  for (size_t i = 0; i < fts_matches.size(); i++) {
1432    if (options.max_count != 0 &&
1433        static_cast<int>(result->size()) >= options.max_count)
1434      break;  // Got too many items.
1435
1436    // Get the URL, querying the main and archived databases as necessary. If
1437    // this is not found, the history and full text search databases are out
1438    // of sync and we give up with this result.
1439    if (!querier.GetRowForURL(fts_matches[i].url, &url_result))
1440      continue;
1441
1442    if (!url_result.url().is_valid())
1443      continue;  // Don't report invalid URLs in case of corruption.
1444
1445    // Copy over the FTS stuff that the URLDatabase doesn't know about.
1446    // We do this with swap() to avoid copying, since we know we don't
1447    // need the original any more. Note that we override the title with the
1448    // one from FTS, since that will match the title_match_positions (the
1449    // FTS title and the history DB title may differ).
1450    url_result.set_title(fts_matches[i].title);
1451    url_result.title_match_positions_.swap(
1452        fts_matches[i].title_match_positions);
1453    url_result.snippet_.Swap(&fts_matches[i].snippet);
1454
1455    // The visit time also comes from the full text search database. Since it
1456    // has the time, we can avoid an extra query of the visits table.
1457    url_result.set_visit_time(fts_matches[i].time);
1458
1459    // Add it to the vector, this will clear our |url_row| object as a
1460    // result of the swap.
1461    result->AppendURLBySwapping(&url_result);
1462  }
1463
1464  if (options.begin_time <= first_recorded_time_)
1465    result->set_reached_beginning(true);
1466}
1467
1468// Frontend to GetMostRecentRedirectsFrom from the history thread.
1469void HistoryBackend::QueryRedirectsFrom(
1470    scoped_refptr<QueryRedirectsRequest> request,
1471    const GURL& url) {
1472  if (request->canceled())
1473    return;
1474  bool success = GetMostRecentRedirectsFrom(url, &request->value);
1475  request->ForwardResult(request->handle(), url, success, &request->value);
1476}
1477
1478void HistoryBackend::QueryRedirectsTo(
1479    scoped_refptr<QueryRedirectsRequest> request,
1480    const GURL& url) {
1481  if (request->canceled())
1482    return;
1483  bool success = GetMostRecentRedirectsTo(url, &request->value);
1484  request->ForwardResult(request->handle(), url, success, &request->value);
1485}
1486
1487void HistoryBackend::GetVisibleVisitCountToHost(
1488    scoped_refptr<GetVisibleVisitCountToHostRequest> request,
1489    const GURL& url) {
1490  if (request->canceled())
1491    return;
1492  int count = 0;
1493  Time first_visit;
1494  const bool success = db_.get() &&
1495      db_->GetVisibleVisitCountToHost(url, &count, &first_visit);
1496  request->ForwardResult(request->handle(), success, count, first_visit);
1497}
1498
1499void HistoryBackend::QueryTopURLsAndRedirects(
1500    scoped_refptr<QueryTopURLsAndRedirectsRequest> request,
1501    int result_count) {
1502  if (request->canceled())
1503    return;
1504
1505  if (!db_.get()) {
1506    request->ForwardResult(request->handle(), false, NULL, NULL);
1507    return;
1508  }
1509
1510  std::vector<GURL>* top_urls = &request->value.a;
1511  history::RedirectMap* redirects = &request->value.b;
1512
1513  ScopedVector<PageUsageData> data;
1514  db_->QuerySegmentUsage(base::Time::Now() - base::TimeDelta::FromDays(90),
1515      result_count, &data.get());
1516
1517  for (size_t i = 0; i < data.size(); ++i) {
1518    top_urls->push_back(data[i]->GetURL());
1519    RefCountedVector<GURL>* list = new RefCountedVector<GURL>;
1520    GetMostRecentRedirectsFrom(top_urls->back(), &list->data);
1521    (*redirects)[top_urls->back()] = list;
1522  }
1523
1524  request->ForwardResult(request->handle(), true, top_urls, redirects);
1525}
1526
1527// Will replace QueryTopURLsAndRedirectsRequest.
1528void HistoryBackend::QueryMostVisitedURLs(
1529    scoped_refptr<QueryMostVisitedURLsRequest> request,
1530    int result_count,
1531    int days_back) {
1532  if (request->canceled())
1533    return;
1534
1535  if (!db_.get()) {
1536    // No History Database - return an empty list.
1537    request->ForwardResult(request->handle(), MostVisitedURLList());
1538    return;
1539  }
1540
1541  MostVisitedURLList* result = &request->value;
1542  QueryMostVisitedURLsImpl(result_count, days_back, result);
1543  request->ForwardResult(request->handle(), *result);
1544}
1545
1546void HistoryBackend::QueryFilteredURLs(
1547      scoped_refptr<QueryFilteredURLsRequest> request,
1548      int result_count,
1549      const history::VisitFilter& filter,
1550      bool extended_info)  {
1551  if (request->canceled())
1552    return;
1553
1554  base::Time request_start = base::Time::Now();
1555
1556  if (!db_.get()) {
1557    // No History Database - return an empty list.
1558    request->ForwardResult(request->handle(), FilteredURLList());
1559    return;
1560  }
1561
1562  VisitVector visits;
1563  db_->GetDirectVisitsDuringTimes(filter, 0, &visits);
1564
1565  std::map<URLID, double> score_map;
1566  for (size_t i = 0; i < visits.size(); ++i) {
1567    score_map[visits[i].url_id] += filter.GetVisitScore(visits[i]);
1568  }
1569
1570  // TODO(georgey): experiment with visit_segment database granularity (it is
1571  // currently 24 hours) to use it directly instead of using visits database,
1572  // which is considerably slower.
1573  ScopedVector<PageUsageData> data;
1574  data.reserve(score_map.size());
1575  for (std::map<URLID, double>::iterator it = score_map.begin();
1576       it != score_map.end(); ++it) {
1577    PageUsageData* pud = new PageUsageData(it->first);
1578    pud->SetScore(it->second);
1579    data.push_back(pud);
1580  }
1581
1582  // Limit to the top |result_count| results.
1583  std::sort(data.begin(), data.end(), PageUsageData::Predicate);
1584  if (result_count && static_cast<int>(data.size()) > result_count) {
1585    STLDeleteContainerPointers(data.begin() + result_count, data.end());
1586    data.resize(result_count);
1587  }
1588
1589  for (size_t i = 0; i < data.size(); ++i) {
1590    URLRow info;
1591    if (db_->GetURLRow(data[i]->GetID(), &info)) {
1592      data[i]->SetURL(info.url());
1593      data[i]->SetTitle(info.title());
1594    }
1595  }
1596
1597  FilteredURLList& result = request->value;
1598  for (size_t i = 0; i < data.size(); ++i) {
1599    PageUsageData* current_data = data[i];
1600    FilteredURL url(*current_data);
1601
1602    if (extended_info) {
1603      VisitVector visits;
1604      db_->GetVisitsForURL(current_data->GetID(), &visits);
1605      if (visits.size() > 0) {
1606        url.extended_info.total_visits = visits.size();
1607        for (size_t i = 0; i < visits.size(); ++i) {
1608          url.extended_info.duration_opened +=
1609              visits[i].visit_duration.InSeconds();
1610          if (visits[i].visit_time > url.extended_info.last_visit_time) {
1611            url.extended_info.last_visit_time = visits[i].visit_time;
1612          }
1613        }
1614        // TODO(macourteau): implement the url.extended_info.visits stat.
1615      }
1616    }
1617    result.push_back(url);
1618  }
1619
1620  int delta_time = std::max(1, std::min(999,
1621      static_cast<int>((base::Time::Now() - request_start).InMilliseconds())));
1622  STATIC_HISTOGRAM_POINTER_BLOCK(
1623      "NewTabPage.SuggestedSitesLoadTime",
1624      Add(delta_time),
1625      base::LinearHistogram::FactoryGet("NewTabPage.SuggestedSitesLoadTime",
1626          1, 1000, 100, base::Histogram::kUmaTargetedHistogramFlag));
1627
1628  request->ForwardResult(request->handle(), result);
1629}
1630
1631void HistoryBackend::QueryMostVisitedURLsImpl(int result_count,
1632                                              int days_back,
1633                                              MostVisitedURLList* result) {
1634  if (!db_.get())
1635    return;
1636
1637  ScopedVector<PageUsageData> data;
1638  db_->QuerySegmentUsage(base::Time::Now() -
1639                         base::TimeDelta::FromDays(days_back),
1640                         result_count, &data.get());
1641
1642  for (size_t i = 0; i < data.size(); ++i) {
1643    PageUsageData* current_data = data[i];
1644    RedirectList redirects;
1645    GetMostRecentRedirectsFrom(current_data->GetURL(), &redirects);
1646    MostVisitedURL url = MakeMostVisitedURL(*current_data, redirects);
1647    result->push_back(url);
1648  }
1649}
1650
1651void HistoryBackend::GetRedirectsFromSpecificVisit(
1652    VisitID cur_visit, history::RedirectList* redirects) {
1653  // Follow any redirects from the given visit and add them to the list.
1654  // It *should* be impossible to get a circular chain here, but we check
1655  // just in case to avoid infinite loops.
1656  GURL cur_url;
1657  std::set<VisitID> visit_set;
1658  visit_set.insert(cur_visit);
1659  while (db_->GetRedirectFromVisit(cur_visit, &cur_visit, &cur_url)) {
1660    if (visit_set.find(cur_visit) != visit_set.end()) {
1661      NOTREACHED() << "Loop in visit chain, giving up";
1662      return;
1663    }
1664    visit_set.insert(cur_visit);
1665    redirects->push_back(cur_url);
1666  }
1667}
1668
1669void HistoryBackend::GetRedirectsToSpecificVisit(
1670    VisitID cur_visit,
1671    history::RedirectList* redirects) {
1672  // Follow redirects going to cur_visit. These are added to |redirects| in
1673  // the order they are found. If a redirect chain looks like A -> B -> C and
1674  // |cur_visit| = C, redirects will be {B, A} in that order.
1675  if (!db_.get())
1676    return;
1677
1678  GURL cur_url;
1679  std::set<VisitID> visit_set;
1680  visit_set.insert(cur_visit);
1681  while (db_->GetRedirectToVisit(cur_visit, &cur_visit, &cur_url)) {
1682    if (visit_set.find(cur_visit) != visit_set.end()) {
1683      NOTREACHED() << "Loop in visit chain, giving up";
1684      return;
1685    }
1686    visit_set.insert(cur_visit);
1687    redirects->push_back(cur_url);
1688  }
1689}
1690
1691bool HistoryBackend::GetMostRecentRedirectsFrom(
1692    const GURL& from_url,
1693    history::RedirectList* redirects) {
1694  redirects->clear();
1695  if (!db_.get())
1696    return false;
1697
1698  URLID from_url_id = db_->GetRowForURL(from_url, NULL);
1699  VisitID cur_visit = db_->GetMostRecentVisitForURL(from_url_id, NULL);
1700  if (!cur_visit)
1701    return false;  // No visits for URL.
1702
1703  GetRedirectsFromSpecificVisit(cur_visit, redirects);
1704  return true;
1705}
1706
1707bool HistoryBackend::GetMostRecentRedirectsTo(
1708    const GURL& to_url,
1709    history::RedirectList* redirects) {
1710  redirects->clear();
1711  if (!db_.get())
1712    return false;
1713
1714  URLID to_url_id = db_->GetRowForURL(to_url, NULL);
1715  VisitID cur_visit = db_->GetMostRecentVisitForURL(to_url_id, NULL);
1716  if (!cur_visit)
1717    return false;  // No visits for URL.
1718
1719  GetRedirectsToSpecificVisit(cur_visit, redirects);
1720  return true;
1721}
1722
1723void HistoryBackend::ScheduleAutocomplete(HistoryURLProvider* provider,
1724                                          HistoryURLProviderParams* params) {
1725  // ExecuteWithDB should handle the NULL database case.
1726  provider->ExecuteWithDB(this, db_.get(), params);
1727}
1728
1729void HistoryBackend::SetPageContents(const GURL& url,
1730                                     const string16& contents) {
1731  // This is histogrammed in the text database manager.
1732  if (!text_database_.get())
1733    return;
1734  text_database_->AddPageContents(url, contents);
1735}
1736
1737void HistoryBackend::SetPageThumbnail(
1738    const GURL& url,
1739    const gfx::Image* thumbnail,
1740    const ThumbnailScore& score) {
1741  if (!db_.get() || !thumbnail_db_.get())
1742    return;
1743
1744  URLRow url_row;
1745  URLID url_id = db_->GetRowForURL(url, &url_row);
1746  if (url_id) {
1747    thumbnail_db_->SetPageThumbnail(url, url_id, thumbnail, score,
1748                                    url_row.last_visit());
1749  }
1750
1751  ScheduleCommit();
1752}
1753
1754void HistoryBackend::GetPageThumbnail(
1755    scoped_refptr<GetPageThumbnailRequest> request,
1756    const GURL& page_url) {
1757  if (request->canceled())
1758    return;
1759
1760  scoped_refptr<base::RefCountedBytes> data;
1761  GetPageThumbnailDirectly(page_url, &data);
1762
1763  request->ForwardResult(request->handle(), data);
1764}
1765
1766void HistoryBackend::GetPageThumbnailDirectly(
1767    const GURL& page_url,
1768    scoped_refptr<base::RefCountedBytes>* data) {
1769  if (thumbnail_db_.get()) {
1770    *data = new base::RefCountedBytes;
1771
1772    // Time the result.
1773    TimeTicks beginning_time = TimeTicks::Now();
1774
1775    history::RedirectList redirects;
1776    URLID url_id;
1777    bool success = false;
1778
1779    // If there are some redirects, try to get a thumbnail from the last
1780    // redirect destination.
1781    if (GetMostRecentRedirectsFrom(page_url, &redirects) &&
1782        !redirects.empty()) {
1783      if ((url_id = db_->GetRowForURL(redirects.back(), NULL)))
1784        success = thumbnail_db_->GetPageThumbnail(url_id, &(*data)->data());
1785    }
1786
1787    // If we don't have a thumbnail from redirects, try the URL directly.
1788    if (!success) {
1789      if ((url_id = db_->GetRowForURL(page_url, NULL)))
1790        success = thumbnail_db_->GetPageThumbnail(url_id, &(*data)->data());
1791    }
1792
1793    // In this rare case, we start to mine the older redirect sessions
1794    // from the visit table to try to find a thumbnail.
1795    if (!success) {
1796      success = GetThumbnailFromOlderRedirect(page_url, &(*data)->data());
1797    }
1798
1799    if (!success)
1800      *data = NULL;  // This will tell the callback there was an error.
1801
1802    UMA_HISTOGRAM_TIMES("History.GetPageThumbnail",
1803                        TimeTicks::Now() - beginning_time);
1804  }
1805}
1806
1807void HistoryBackend::MigrateThumbnailsDatabase() {
1808  // If there is no History DB, we can't record that the migration was done.
1809  // It will be recorded on the next run.
1810  if (db_.get()) {
1811    // If there is no thumbnail DB, we can still record a successful migration.
1812    if (thumbnail_db_.get()) {
1813      thumbnail_db_->RenameAndDropThumbnails(GetThumbnailFileName(),
1814                                             GetFaviconsFileName());
1815    }
1816    db_->ThumbnailMigrationDone();
1817  }
1818}
1819
1820bool HistoryBackend::GetThumbnailFromOlderRedirect(
1821    const GURL& page_url,
1822    std::vector<unsigned char>* data) {
1823  // Look at a few previous visit sessions.
1824  VisitVector older_sessions;
1825  URLID page_url_id = db_->GetRowForURL(page_url, NULL);
1826  static const int kVisitsToSearchForThumbnail = 4;
1827  db_->GetMostRecentVisitsForURL(
1828      page_url_id, kVisitsToSearchForThumbnail, &older_sessions);
1829
1830  // Iterate across all those previous visits, and see if any of the
1831  // final destinations of those redirect chains have a good thumbnail
1832  // for us.
1833  bool success = false;
1834  for (VisitVector::const_iterator it = older_sessions.begin();
1835       !success && it != older_sessions.end(); ++it) {
1836    history::RedirectList redirects;
1837    if (it->visit_id) {
1838      GetRedirectsFromSpecificVisit(it->visit_id, &redirects);
1839
1840      if (!redirects.empty()) {
1841        URLID url_id;
1842        if ((url_id = db_->GetRowForURL(redirects.back(), NULL)))
1843          success = thumbnail_db_->GetPageThumbnail(url_id, data);
1844      }
1845    }
1846  }
1847
1848  return success;
1849}
1850
1851void HistoryBackend::GetFavicons(
1852    scoped_refptr<GetFaviconRequest> request,
1853    const std::vector<GURL>& icon_urls,
1854    int icon_types,
1855    int desired_size_in_dip,
1856    const std::vector<ui::ScaleFactor>& desired_scale_factors) {
1857  UpdateFaviconMappingsAndFetchImpl(request, NULL, icon_urls, icon_types,
1858      desired_size_in_dip, desired_scale_factors);
1859}
1860
1861void HistoryBackend::GetFaviconsForURL(
1862    scoped_refptr<GetFaviconRequest> request,
1863    const GURL& page_url,
1864    int icon_types,
1865    int desired_size_in_dip,
1866    const std::vector<ui::ScaleFactor>& desired_scale_factors) {
1867  if (request->canceled())
1868    return;
1869
1870  std::vector<FaviconBitmapResult> favicon_bitmap_results;
1871  IconURLSizesMap icon_url_sizes;
1872
1873  // Get results from DB.
1874  GetFaviconsFromDB(page_url, icon_types, desired_size_in_dip,
1875      desired_scale_factors, &favicon_bitmap_results, &icon_url_sizes);
1876
1877  request->ForwardResult(request->handle(), favicon_bitmap_results,
1878                         icon_url_sizes);
1879}
1880
1881void HistoryBackend::GetFaviconForID(scoped_refptr<GetFaviconRequest> request,
1882                                     FaviconID favicon_id,
1883                                     int desired_size_in_dip,
1884                                     ui::ScaleFactor desired_scale_factor) {
1885  if (request->canceled())
1886    return;
1887
1888  std::vector<FaviconID> favicon_ids;
1889  favicon_ids.push_back(favicon_id);
1890  std::vector<ui::ScaleFactor> desired_scale_factors;
1891  desired_scale_factors.push_back(desired_scale_factor);
1892
1893  // Get results from DB.
1894  std::vector<FaviconBitmapResult> favicon_bitmap_results;
1895  GetFaviconBitmapResultsForBestMatch(favicon_ids, desired_size_in_dip,
1896      desired_scale_factors, &favicon_bitmap_results);
1897
1898  IconURLSizesMap icon_url_sizes;
1899  BuildIconURLSizesMap(favicon_ids, &icon_url_sizes);
1900
1901  request->ForwardResult(request->handle(), favicon_bitmap_results,
1902                         icon_url_sizes);
1903}
1904
1905void HistoryBackend::UpdateFaviconMappingsAndFetch(
1906    scoped_refptr<GetFaviconRequest> request,
1907    const GURL& page_url,
1908    const std::vector<GURL>& icon_urls,
1909    int icon_types,
1910    int desired_size_in_dip,
1911    const std::vector<ui::ScaleFactor>& desired_scale_factors) {
1912  UpdateFaviconMappingsAndFetchImpl(request, &page_url, icon_urls, icon_types,
1913      desired_size_in_dip, desired_scale_factors);
1914}
1915
1916void HistoryBackend::MergeFavicon(
1917    const GURL& page_url,
1918    history::IconType icon_type,
1919    scoped_refptr<base::RefCountedMemory> bitmap_data,
1920    const gfx::Size& pixel_size) {
1921  if (!thumbnail_db_.get() || !db_.get())
1922    return;
1923
1924  std::vector<IconMapping> icon_mappings;
1925  thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type, &icon_mappings);
1926
1927  for (size_t i = 0; i < icon_mappings.size(); ++i) {
1928    std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
1929    thumbnail_db_->GetFaviconBitmapIDSizes(icon_mappings[i].icon_id,
1930                                           &bitmap_id_sizes);
1931
1932    for (size_t j = 0; j < bitmap_id_sizes.size(); ++j) {
1933      if (bitmap_id_sizes[j].pixel_size == pixel_size) {
1934        // There is a favicon bitmap of |pixel_size| already mapped to
1935        // |page_url|, replace it.
1936        thumbnail_db_->SetFaviconBitmap(bitmap_id_sizes[j].bitmap_id,
1937            bitmap_data, base::Time::Now());
1938
1939        // Send notification to the UI that the favicon bitmap was updated.
1940        SendFaviconChangedNotificationForPageAndRedirects(page_url);
1941        ScheduleCommit();
1942        return;
1943      }
1944    }
1945  }
1946
1947  // There is no exact match for |pixel_size|. Create a new favicon with a fake
1948  // icon URL. Use |page_url| as the fake icon URL as it is guaranteed to be
1949  // unique.
1950  const GURL& fake_icon_url = page_url;
1951
1952  // There may already be a favicon with |fake_icon_url| mapped to |page_url|.
1953  // This will be the case if MergeFavicon() was previously called for
1954  // |page_url| with a different pixel size. Reuse the favicon if it exists.
1955  FaviconID fake_icon_id = 0;
1956  for (size_t i = 0; i < icon_mappings.size(); ++i) {
1957    if (icon_mappings[i].icon_url == fake_icon_url)
1958      fake_icon_id = icon_mappings[i].icon_id;
1959  }
1960
1961  bool update_mappings = false;
1962  if (!fake_icon_id) {
1963    fake_icon_id = thumbnail_db_->AddFavicon(fake_icon_url, icon_type,
1964                                             GetDefaultFaviconSizes());
1965
1966    // The favicon mappings need to be updated to include the new favicon.
1967    update_mappings = true;
1968  }
1969
1970  // Remove an arbitrary favicon bitmap to avoid going over the limit of
1971  // |kMaxFaviconBitmapsPerIconURL|.
1972  std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
1973  thumbnail_db_->GetFaviconBitmapIDSizes(fake_icon_id, &bitmap_id_sizes);
1974  if (bitmap_id_sizes.size() == kMaxFaviconBitmapsPerIconURL)
1975    thumbnail_db_->DeleteFaviconBitmap(bitmap_id_sizes[0].bitmap_id);
1976
1977  thumbnail_db_->AddFaviconBitmap(fake_icon_id, bitmap_data, base::Time::Now(),
1978                                  pixel_size);
1979
1980  if (update_mappings) {
1981    // FaviconIDs which should be mapped to |page_url| for |icon_type|.
1982    std::vector<FaviconID> favicon_ids;
1983    for (size_t i = 0; i < icon_mappings.size(); ++i)
1984      favicon_ids.push_back(icon_mappings[i].icon_id);
1985
1986    // Remove an arbitrary favicon to avoid going over the limit of
1987    // |kMaxFaviconsPerPage|.
1988    if (favicon_ids.size() == kMaxFaviconsPerPage)
1989      favicon_ids.pop_back();
1990
1991    // Add mapping to |fake_icon_id|.
1992    favicon_ids.push_back(fake_icon_id);
1993    SetFaviconMappingsForPageAndRedirects(page_url, icon_type, favicon_ids);
1994  }
1995
1996  // Send notification to the UI as at least a favicon bitmap was added.
1997  SendFaviconChangedNotificationForPageAndRedirects(page_url);
1998  ScheduleCommit();
1999}
2000
2001void HistoryBackend::SetFavicons(
2002    const GURL& page_url,
2003    IconType icon_type,
2004    const std::vector<FaviconBitmapData>& favicon_bitmap_data,
2005    const IconURLSizesMap& icon_url_sizes) {
2006  if (!thumbnail_db_.get() || !db_.get())
2007    return;
2008
2009  DCHECK(ValidateSetFaviconsParams(favicon_bitmap_data, icon_url_sizes));
2010
2011  // Build map of FaviconBitmapData for each icon url.
2012  typedef std::map<GURL, std::vector<FaviconBitmapData> >
2013      BitmapDataByIconURL;
2014  BitmapDataByIconURL grouped_by_icon_url;
2015  for (size_t i = 0; i < favicon_bitmap_data.size(); ++i) {
2016    const GURL& icon_url = favicon_bitmap_data[i].icon_url;
2017    grouped_by_icon_url[icon_url].push_back(favicon_bitmap_data[i]);
2018  }
2019
2020  std::vector<FaviconID> icon_ids;
2021  for (IconURLSizesMap::const_iterator it = icon_url_sizes.begin();
2022       it != icon_url_sizes.end(); ++it) {
2023    const GURL& icon_url = it->first;
2024    FaviconID icon_id =
2025        thumbnail_db_->GetFaviconIDForFaviconURL(icon_url, icon_type, NULL);
2026    if (icon_id)
2027      SetFaviconSizes(icon_id, it->second);
2028    else
2029      icon_id = thumbnail_db_->AddFavicon(icon_url, icon_type, it->second);
2030    icon_ids.push_back(icon_id);
2031
2032    BitmapDataByIconURL::iterator grouped_by_icon_url_it =
2033        grouped_by_icon_url.find(icon_url);
2034    if (grouped_by_icon_url_it != grouped_by_icon_url.end())
2035      SetFaviconBitmaps(icon_id, grouped_by_icon_url_it->second);
2036  }
2037
2038  SetFaviconMappingsForPageAndRedirects(page_url, icon_type, icon_ids);
2039
2040  // Send notification to the UI as an icon mapping, favicon, or favicon bitmap
2041  // almost certainly was changed by this function. The situations where no
2042  // data was changed, notably when |favicon_bitmap_data| is empty do not occur
2043  // in practice.
2044  SendFaviconChangedNotificationForPageAndRedirects(page_url);
2045  ScheduleCommit();
2046}
2047
2048void HistoryBackend::SetFaviconsOutOfDateForPage(const GURL& page_url) {
2049  std::vector<IconMapping> icon_mappings;
2050
2051  if (!thumbnail_db_.get() ||
2052      !thumbnail_db_->GetIconMappingsForPageURL(page_url,
2053                                                &icon_mappings))
2054    return;
2055
2056  for (std::vector<IconMapping>::iterator m = icon_mappings.begin();
2057       m != icon_mappings.end(); ++m) {
2058    thumbnail_db_->SetFaviconOutOfDate(m->icon_id);
2059  }
2060  ScheduleCommit();
2061}
2062
2063void HistoryBackend::CloneFavicons(const GURL& old_page_url,
2064                                   const GURL& new_page_url) {
2065  if (!thumbnail_db_.get())
2066    return;
2067
2068  // Prevent cross-domain cloning.
2069  if (old_page_url.GetOrigin() != new_page_url.GetOrigin())
2070    return;
2071
2072  thumbnail_db_->CloneIconMappings(old_page_url, new_page_url);
2073  ScheduleCommit();
2074}
2075
2076void HistoryBackend::SetImportedFavicons(
2077    const std::vector<ImportedFaviconUsage>& favicon_usage) {
2078  if (!db_.get() || !thumbnail_db_.get())
2079    return;
2080
2081  Time now = Time::Now();
2082
2083  // Track all URLs that had their favicons set or updated.
2084  std::set<GURL> favicons_changed;
2085
2086  for (size_t i = 0; i < favicon_usage.size(); i++) {
2087    FaviconID favicon_id = thumbnail_db_->GetFaviconIDForFaviconURL(
2088        favicon_usage[i].favicon_url, history::FAVICON, NULL);
2089    if (!favicon_id) {
2090      // This favicon doesn't exist yet, so we create it using the given data.
2091      // TODO(pkotwicz): Pass in real pixel size.
2092      favicon_id = thumbnail_db_->AddFavicon(
2093          favicon_usage[i].favicon_url,
2094          history::FAVICON,
2095          GetDefaultFaviconSizes(),
2096          new base::RefCountedBytes(favicon_usage[i].png_data),
2097          now,
2098          gfx::Size());
2099    }
2100
2101    // Save the mapping from all the URLs to the favicon.
2102    BookmarkService* bookmark_service = GetBookmarkService();
2103    for (std::set<GURL>::const_iterator url = favicon_usage[i].urls.begin();
2104         url != favicon_usage[i].urls.end(); ++url) {
2105      URLRow url_row;
2106      if (!db_->GetRowForURL(*url, &url_row)) {
2107        // If the URL is present as a bookmark, add the url in history to
2108        // save the favicon mapping. This will match with what history db does
2109        // for regular bookmarked URLs with favicons - when history db is
2110        // cleaned, we keep an entry in the db with 0 visits as long as that
2111        // url is bookmarked.
2112        if (bookmark_service && bookmark_service_->IsBookmarked(*url)) {
2113          URLRow url_info(*url);
2114          url_info.set_visit_count(0);
2115          url_info.set_typed_count(0);
2116          url_info.set_last_visit(base::Time());
2117          url_info.set_hidden(false);
2118          db_->AddURL(url_info);
2119          thumbnail_db_->AddIconMapping(*url, favicon_id);
2120          favicons_changed.insert(*url);
2121        }
2122      } else {
2123        if (!thumbnail_db_->GetIconMappingsForPageURL(*url, FAVICON, NULL)) {
2124          // URL is present in history, update the favicon *only* if it is not
2125          // set already.
2126          thumbnail_db_->AddIconMapping(*url, favicon_id);
2127          favicons_changed.insert(*url);
2128        }
2129      }
2130    }
2131  }
2132
2133  if (!favicons_changed.empty()) {
2134    // Send the notification about the changed favicon URLs.
2135    FaviconChangeDetails* changed_details = new FaviconChangeDetails;
2136    changed_details->urls.swap(favicons_changed);
2137    BroadcastNotifications(chrome::NOTIFICATION_FAVICON_CHANGED,
2138                           changed_details);
2139  }
2140}
2141
2142void HistoryBackend::UpdateFaviconMappingsAndFetchImpl(
2143    scoped_refptr<GetFaviconRequest> request,
2144    const GURL* page_url,
2145    const std::vector<GURL>& icon_urls,
2146    int icon_types,
2147    int desired_size_in_dip,
2148    const std::vector<ui::ScaleFactor>& desired_scale_factors) {
2149  // If |page_url| is specified, |icon_types| must be either a single icon
2150  // type or icon types which are equivalent.
2151  DCHECK(!page_url ||
2152         icon_types == FAVICON ||
2153         icon_types == TOUCH_ICON ||
2154         icon_types == TOUCH_PRECOMPOSED_ICON ||
2155         icon_types == (TOUCH_ICON | TOUCH_PRECOMPOSED_ICON));
2156
2157  if (request->canceled())
2158    return;
2159
2160  if (!thumbnail_db_.get()) {
2161    // The thumbnail database is not valid. Send response to the UI as it still
2162    // expects one.
2163    request->ForwardResult(request->handle(),
2164                           std::vector<history::FaviconBitmapResult>(),
2165                           history::IconURLSizesMap());
2166    return;
2167  }
2168
2169  std::vector<FaviconID> favicon_ids;
2170
2171  // The icon type for which the mappings will the updated and data will be
2172  // returned.
2173  IconType selected_icon_type = INVALID_ICON;
2174
2175  for (size_t i = 0; i < icon_urls.size(); ++i) {
2176    const GURL& icon_url = icon_urls[i];
2177    IconType icon_type_out;
2178    const FaviconID favicon_id = thumbnail_db_->GetFaviconIDForFaviconURL(
2179        icon_url, icon_types, &icon_type_out);
2180
2181    if (favicon_id) {
2182      // Return and update icon mappings only for the largest icon type. As
2183      // |icon_urls| is not sorted in terms of icon type, clear |favicon_ids|
2184      // if an |icon_url| with a larger icon type is found.
2185      if (icon_type_out > selected_icon_type) {
2186        selected_icon_type = icon_type_out;
2187        favicon_ids.clear();
2188      }
2189      if (icon_type_out == selected_icon_type)
2190        favicon_ids.push_back(favicon_id);
2191    }
2192  }
2193
2194  if (page_url && !favicon_ids.empty()) {
2195    bool mappings_updated =
2196        SetFaviconMappingsForPageAndRedirects(*page_url, selected_icon_type,
2197                                              favicon_ids);
2198    if (mappings_updated) {
2199      SendFaviconChangedNotificationForPageAndRedirects(*page_url);
2200      ScheduleCommit();
2201    }
2202  }
2203
2204  std::vector<FaviconBitmapResult> favicon_bitmap_results;
2205  GetFaviconBitmapResultsForBestMatch(favicon_ids, desired_size_in_dip,
2206      desired_scale_factors, &favicon_bitmap_results);
2207  IconURLSizesMap icon_url_sizes;
2208  BuildIconURLSizesMap(favicon_ids, &icon_url_sizes);
2209
2210  request->ForwardResult(request->handle(), favicon_bitmap_results,
2211                         icon_url_sizes);
2212}
2213
2214void HistoryBackend::SetFaviconBitmaps(
2215    FaviconID icon_id,
2216    const std::vector<FaviconBitmapData>& favicon_bitmap_data) {
2217  std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
2218  thumbnail_db_->GetFaviconBitmapIDSizes(icon_id, &bitmap_id_sizes);
2219
2220  // A nested loop is ok because in practice neither |favicon_bitmap_data| nor
2221  // |bitmap_id_sizes| will have many elements.
2222  for (size_t i = 0; i < favicon_bitmap_data.size(); ++i) {
2223    const FaviconBitmapData& bitmap_data_element = favicon_bitmap_data[i];
2224    FaviconBitmapID bitmap_id = 0;
2225    for (size_t j = 0; j < bitmap_id_sizes.size(); ++j) {
2226      if (bitmap_id_sizes[j].pixel_size == bitmap_data_element.pixel_size) {
2227        bitmap_id = bitmap_id_sizes[j].bitmap_id;
2228        break;
2229      }
2230    }
2231    if (bitmap_id) {
2232      thumbnail_db_->SetFaviconBitmap(bitmap_id,
2233          bitmap_data_element.bitmap_data, base::Time::Now());
2234    } else {
2235      thumbnail_db_->AddFaviconBitmap(icon_id, bitmap_data_element.bitmap_data,
2236          base::Time::Now(), bitmap_data_element.pixel_size);
2237    }
2238  }
2239}
2240
2241bool HistoryBackend::ValidateSetFaviconsParams(
2242    const std::vector<FaviconBitmapData>& favicon_bitmap_data,
2243    const IconURLSizesMap& icon_url_sizes) const {
2244  if (icon_url_sizes.size() > kMaxFaviconsPerPage)
2245    return false;
2246
2247  for (IconURLSizesMap::const_iterator it = icon_url_sizes.begin();
2248       it != icon_url_sizes.end(); ++it) {
2249    if (it->second.size() > kMaxFaviconBitmapsPerIconURL)
2250      return false;
2251  }
2252
2253  for (size_t i = 0; i < favicon_bitmap_data.size(); ++i) {
2254    if (!favicon_bitmap_data[i].bitmap_data.get())
2255      return false;
2256
2257    IconURLSizesMap::const_iterator it =
2258        icon_url_sizes.find(favicon_bitmap_data[i].icon_url);
2259    if (it == icon_url_sizes.end())
2260      return false;
2261
2262    const FaviconSizes& favicon_sizes = it->second;
2263    FaviconSizes::const_iterator it2 = std::find(favicon_sizes.begin(),
2264        favicon_sizes.end(), favicon_bitmap_data[i].pixel_size);
2265    if (it2 == favicon_sizes.end())
2266      return false;
2267  }
2268  return true;
2269}
2270
2271void HistoryBackend::SetFaviconSizes(FaviconID icon_id,
2272                                     const FaviconSizes& favicon_sizes) {
2273  std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
2274  thumbnail_db_->GetFaviconBitmapIDSizes(icon_id, &bitmap_id_sizes);
2275
2276  // Remove bitmaps whose pixel size is not contained in |favicon_sizes|.
2277  for (size_t i = 0; i < bitmap_id_sizes.size(); ++i) {
2278    const gfx::Size& pixel_size = bitmap_id_sizes[i].pixel_size;
2279    FaviconSizes::const_iterator sizes_it = std::find(favicon_sizes.begin(),
2280        favicon_sizes.end(), pixel_size);
2281    if (sizes_it == favicon_sizes.end())
2282      thumbnail_db_->DeleteFaviconBitmap(bitmap_id_sizes[i].bitmap_id);
2283  }
2284
2285  thumbnail_db_->SetFaviconSizes(icon_id, favicon_sizes);
2286}
2287
2288bool HistoryBackend::GetFaviconsFromDB(
2289    const GURL& page_url,
2290    int icon_types,
2291    int desired_size_in_dip,
2292    const std::vector<ui::ScaleFactor>& desired_scale_factors,
2293    std::vector<FaviconBitmapResult>* favicon_bitmap_results,
2294    IconURLSizesMap* icon_url_sizes) {
2295  DCHECK(favicon_bitmap_results);
2296  DCHECK(icon_url_sizes);
2297
2298  if (!db_.get() || !thumbnail_db_.get())
2299    return false;
2300
2301  // Time the query.
2302  TimeTicks beginning_time = TimeTicks::Now();
2303
2304  // Get FaviconIDs for |page_url| and one of |icon_types|.
2305  std::vector<IconMapping> icon_mappings;
2306  thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_types,
2307                                           &icon_mappings);
2308  std::vector<FaviconID> favicon_ids;
2309  for (size_t i = 0; i < icon_mappings.size(); ++i)
2310    favicon_ids.push_back(icon_mappings[i].icon_id);
2311
2312  // Populate |favicon_bitmap_results| and |icon_url_sizes|.
2313  bool success =
2314      GetFaviconBitmapResultsForBestMatch(favicon_ids,
2315          desired_size_in_dip, desired_scale_factors, favicon_bitmap_results) &&
2316      BuildIconURLSizesMap(favicon_ids, icon_url_sizes);
2317  UMA_HISTOGRAM_TIMES("History.GetFavIconFromDB",  // historical name
2318                      TimeTicks::Now() - beginning_time);
2319  return success && !icon_url_sizes->empty();
2320}
2321
2322bool HistoryBackend::GetFaviconBitmapResultsForBestMatch(
2323    const std::vector<FaviconID>& candidate_favicon_ids,
2324    int desired_size_in_dip,
2325    const std::vector<ui::ScaleFactor>& desired_scale_factors,
2326    std::vector<history::FaviconBitmapResult>* favicon_bitmap_results) {
2327  favicon_bitmap_results->clear();
2328
2329  if (candidate_favicon_ids.empty())
2330    return true;
2331
2332  // Find the FaviconID and the FaviconBitmapIDs which best match
2333  // |desired_size_in_dip| and |desired_scale_factors|.
2334  // TODO(pkotwicz): Select bitmap results from multiple favicons once
2335  // content::FaviconStatus supports multiple icon URLs.
2336  FaviconID best_favicon_id = 0;
2337  std::vector<FaviconBitmapID> best_bitmap_ids;
2338  float highest_score = kSelectFaviconFramesInvalidScore;
2339  for (size_t i = 0; i < candidate_favicon_ids.size(); ++i) {
2340    std::vector<FaviconBitmapIDSize> bitmap_id_sizes;
2341    thumbnail_db_->GetFaviconBitmapIDSizes(candidate_favicon_ids[i],
2342                                           &bitmap_id_sizes);
2343
2344    // Build vector of gfx::Size from |bitmap_id_sizes|.
2345    std::vector<gfx::Size> sizes;
2346    for (size_t j = 0; j < bitmap_id_sizes.size(); ++j)
2347      sizes.push_back(bitmap_id_sizes[j].pixel_size);
2348
2349    std::vector<size_t> candidate_bitmap_indices;
2350    float score = 0;
2351    SelectFaviconFrameIndices(sizes,
2352                              desired_scale_factors,
2353                              desired_size_in_dip,
2354                              &candidate_bitmap_indices,
2355                              &score);
2356    if (score > highest_score) {
2357      highest_score = score;
2358      best_favicon_id = candidate_favicon_ids[i],
2359      best_bitmap_ids.clear();
2360      for (size_t j = 0; j < candidate_bitmap_indices.size(); ++j) {
2361        size_t candidate_index = candidate_bitmap_indices[j];
2362        best_bitmap_ids.push_back(
2363            bitmap_id_sizes[candidate_index].bitmap_id);
2364      }
2365    }
2366  }
2367
2368  // Construct FaviconBitmapResults from |best_favicon_id| and
2369  // |best_bitmap_ids|.
2370  GURL icon_url;
2371  IconType icon_type;
2372  if (!thumbnail_db_->GetFaviconHeader(best_favicon_id, &icon_url,
2373                                       &icon_type, NULL)) {
2374    return false;
2375  }
2376
2377  for (size_t i = 0; i < best_bitmap_ids.size(); ++i) {
2378    base::Time last_updated;
2379    FaviconBitmapResult bitmap_result;
2380    bitmap_result.icon_url = icon_url;
2381    bitmap_result.icon_type = icon_type;
2382    if (!thumbnail_db_->GetFaviconBitmap(best_bitmap_ids[i],
2383                                         &last_updated,
2384                                         &bitmap_result.bitmap_data,
2385                                         &bitmap_result.pixel_size)) {
2386      return false;
2387    }
2388
2389    bitmap_result.expired = (Time::Now() - last_updated) >
2390        TimeDelta::FromDays(kFaviconRefetchDays);
2391    if (bitmap_result.is_valid())
2392      favicon_bitmap_results->push_back(bitmap_result);
2393  }
2394  return true;
2395}
2396
2397bool HistoryBackend::BuildIconURLSizesMap(
2398    const std::vector<FaviconID>& favicon_ids,
2399    IconURLSizesMap* icon_url_sizes) {
2400  icon_url_sizes->clear();
2401  for (size_t i = 0; i < favicon_ids.size(); ++i) {
2402    GURL icon_url;
2403    FaviconSizes favicon_sizes;
2404    if (!thumbnail_db_->GetFaviconHeader(favicon_ids[i], &icon_url, NULL,
2405                                         &favicon_sizes)) {
2406      return false;
2407    }
2408    (*icon_url_sizes)[icon_url] = favicon_sizes;
2409  }
2410  return true;
2411}
2412
2413bool HistoryBackend::SetFaviconMappingsForPageAndRedirects(
2414    const GURL& page_url,
2415    IconType icon_type,
2416    const std::vector<FaviconID>& icon_ids) {
2417  if (!thumbnail_db_.get())
2418    return false;
2419
2420  // Find all the pages whose favicons we should set, we want to set it for
2421  // all the pages in the redirect chain if it redirected.
2422  history::RedirectList redirects;
2423  GetCachedRecentRedirects(page_url, &redirects);
2424
2425  bool mappings_changed = false;
2426
2427  // Save page <-> favicon associations.
2428  for (history::RedirectList::const_iterator i(redirects.begin());
2429       i != redirects.end(); ++i) {
2430    mappings_changed |= SetFaviconMappingsForPage(*i, icon_type, icon_ids);
2431  }
2432  return mappings_changed;
2433}
2434
2435bool HistoryBackend::SetFaviconMappingsForPage(
2436    const GURL& page_url,
2437    IconType icon_type,
2438    const std::vector<FaviconID>& icon_ids) {
2439  DCHECK_LE(icon_ids.size(), kMaxFaviconsPerPage);
2440  bool mappings_changed = false;
2441
2442  // Two icon types are considered 'equivalent' if one of the icon types is
2443  // TOUCH_ICON and the other is TOUCH_PRECOMPOSED_ICON.
2444  //
2445  // Sets the icon mappings from |page_url| for |icon_type| to the favicons
2446  // with |icon_ids|. Mappings for |page_url| to favicons of type |icon_type|
2447  // whose FaviconID is not in |icon_ids| are removed. All icon mappings for
2448  // |page_url| to favicons of a type equivalent to |icon_type| are removed.
2449  // Remove any favicons which are orphaned as a result of the removal of the
2450  // icon mappings.
2451
2452  std::vector<FaviconID> unmapped_icon_ids = icon_ids;
2453
2454  std::vector<IconMapping> icon_mappings;
2455  thumbnail_db_->GetIconMappingsForPageURL(page_url, &icon_mappings);
2456
2457  for (std::vector<IconMapping>::iterator m = icon_mappings.begin();
2458       m != icon_mappings.end(); ++m) {
2459    std::vector<FaviconID>::iterator icon_id_it = std::find(
2460        unmapped_icon_ids.begin(), unmapped_icon_ids.end(), m->icon_id);
2461
2462    // If the icon mapping already exists, avoid removing it and adding it back.
2463    if (icon_id_it != unmapped_icon_ids.end()) {
2464      unmapped_icon_ids.erase(icon_id_it);
2465      continue;
2466    }
2467
2468    if ((icon_type == TOUCH_ICON && m->icon_type == TOUCH_PRECOMPOSED_ICON) ||
2469        (icon_type == TOUCH_PRECOMPOSED_ICON && m->icon_type == TOUCH_ICON) ||
2470        (icon_type == m->icon_type)) {
2471      thumbnail_db_->DeleteIconMapping(m->mapping_id);
2472
2473      // Removing the icon mapping may have orphaned the associated favicon so
2474      // we must recheck it. This is not super fast, but this case will get
2475      // triggered rarely, since normally a page will always map to the same
2476      // favicon IDs. It will mostly happen for favicons we import.
2477      if (!thumbnail_db_->HasMappingFor(m->icon_id))
2478        thumbnail_db_->DeleteFavicon(m->icon_id);
2479      mappings_changed = true;
2480    }
2481  }
2482
2483  for (size_t i = 0; i < unmapped_icon_ids.size(); ++i) {
2484    thumbnail_db_->AddIconMapping(page_url, unmapped_icon_ids[i]);
2485    mappings_changed = true;
2486  }
2487  return mappings_changed;
2488}
2489
2490void HistoryBackend::GetCachedRecentRedirects(
2491    const GURL& page_url,
2492    history::RedirectList* redirect_list) {
2493  RedirectCache::iterator iter = recent_redirects_.Get(page_url);
2494  if (iter != recent_redirects_.end()) {
2495    *redirect_list = iter->second;
2496
2497    // The redirect chain should have the destination URL as the last item.
2498    DCHECK(!redirect_list->empty());
2499    DCHECK(redirect_list->back() == page_url);
2500  } else {
2501    // No known redirects, construct mock redirect chain containing |page_url|.
2502    redirect_list->push_back(page_url);
2503  }
2504}
2505
2506void HistoryBackend::SendFaviconChangedNotificationForPageAndRedirects(
2507    const GURL& page_url) {
2508  history::RedirectList redirect_list;
2509  GetCachedRecentRedirects(page_url, &redirect_list);
2510
2511  FaviconChangeDetails* changed_details = new FaviconChangeDetails;
2512  for (size_t i = 0; i < redirect_list.size(); ++i)
2513    changed_details->urls.insert(redirect_list[i]);
2514
2515  BroadcastNotifications(chrome::NOTIFICATION_FAVICON_CHANGED,
2516                         changed_details);
2517}
2518
2519void HistoryBackend::Commit() {
2520  if (!db_.get())
2521    return;
2522
2523  // Note that a commit may not actually have been scheduled if a caller
2524  // explicitly calls this instead of using ScheduleCommit. Likewise, we
2525  // may reset the flag written by a pending commit. But this is OK! It
2526  // will merely cause extra commits (which is kind of the idea). We
2527  // could optimize more for this case (we may get two extra commits in
2528  // some cases) but it hasn't been important yet.
2529  CancelScheduledCommit();
2530
2531  db_->CommitTransaction();
2532  DCHECK(db_->transaction_nesting() == 0) << "Somebody left a transaction open";
2533  db_->BeginTransaction();
2534
2535  if (thumbnail_db_.get()) {
2536    thumbnail_db_->CommitTransaction();
2537    DCHECK(thumbnail_db_->transaction_nesting() == 0) <<
2538        "Somebody left a transaction open";
2539    thumbnail_db_->BeginTransaction();
2540  }
2541
2542  if (archived_db_.get()) {
2543    archived_db_->CommitTransaction();
2544    archived_db_->BeginTransaction();
2545  }
2546
2547  if (text_database_.get()) {
2548    text_database_->CommitTransaction();
2549    text_database_->BeginTransaction();
2550  }
2551}
2552
2553void HistoryBackend::ScheduleCommit() {
2554  if (scheduled_commit_.get())
2555    return;
2556  scheduled_commit_ = new CommitLaterTask(this);
2557  MessageLoop::current()->PostDelayedTask(
2558      FROM_HERE,
2559      base::Bind(&CommitLaterTask::RunCommit, scheduled_commit_.get()),
2560      base::TimeDelta::FromSeconds(kCommitIntervalSeconds));
2561}
2562
2563void HistoryBackend::CancelScheduledCommit() {
2564  if (scheduled_commit_) {
2565    scheduled_commit_->Cancel();
2566    scheduled_commit_ = NULL;
2567  }
2568}
2569
2570void HistoryBackend::ProcessDBTaskImpl() {
2571  if (!db_.get()) {
2572    // db went away, release all the refs.
2573    ReleaseDBTasks();
2574    return;
2575  }
2576
2577  // Remove any canceled tasks.
2578  while (!db_task_requests_.empty() && db_task_requests_.front()->canceled()) {
2579    db_task_requests_.front()->Release();
2580    db_task_requests_.pop_front();
2581  }
2582  if (db_task_requests_.empty())
2583    return;
2584
2585  // Run the first task.
2586  HistoryDBTaskRequest* request = db_task_requests_.front();
2587  db_task_requests_.pop_front();
2588  if (request->value->RunOnDBThread(this, db_.get())) {
2589    // The task is done. Notify the callback.
2590    request->ForwardResult();
2591    // We AddRef'd the request before adding, need to release it now.
2592    request->Release();
2593  } else {
2594    // Tasks wants to run some more. Schedule it at the end of current tasks.
2595    db_task_requests_.push_back(request);
2596    // And process it after an invoke later.
2597    MessageLoop::current()->PostTask(
2598        FROM_HERE, base::Bind(&HistoryBackend::ProcessDBTaskImpl, this));
2599  }
2600}
2601
2602void HistoryBackend::ReleaseDBTasks() {
2603  for (std::list<HistoryDBTaskRequest*>::iterator i =
2604       db_task_requests_.begin(); i != db_task_requests_.end(); ++i) {
2605    (*i)->Release();
2606  }
2607  db_task_requests_.clear();
2608}
2609
2610////////////////////////////////////////////////////////////////////////////////
2611//
2612// Generic operations
2613//
2614////////////////////////////////////////////////////////////////////////////////
2615
2616void HistoryBackend::DeleteURLs(const std::vector<GURL>& urls) {
2617  expirer_.DeleteURLs(urls);
2618
2619  db_->GetStartDate(&first_recorded_time_);
2620  // Force a commit, if the user is deleting something for privacy reasons, we
2621  // want to get it on disk ASAP.
2622  Commit();
2623}
2624
2625void HistoryBackend::DeleteURL(const GURL& url) {
2626  expirer_.DeleteURL(url);
2627
2628  db_->GetStartDate(&first_recorded_time_);
2629  // Force a commit, if the user is deleting something for privacy reasons, we
2630  // want to get it on disk ASAP.
2631  Commit();
2632}
2633
2634void HistoryBackend::ExpireHistoryBetween(
2635    scoped_refptr<CancelableRequest<base::Closure> > request,
2636    const std::set<GURL>& restrict_urls,
2637    Time begin_time,
2638    Time end_time) {
2639  if (request->canceled())
2640    return;
2641
2642  if (db_.get()) {
2643    if (begin_time.is_null() && end_time.is_null() && restrict_urls.empty()) {
2644      // Special case deleting all history so it can be faster and to reduce the
2645      // possibility of an information leak.
2646      DeleteAllHistory();
2647    } else {
2648      // Clearing parts of history, have the expirer do the depend
2649      expirer_.ExpireHistoryBetween(restrict_urls, begin_time, end_time);
2650
2651      // Force a commit, if the user is deleting something for privacy reasons,
2652      // we want to get it on disk ASAP.
2653      Commit();
2654    }
2655  }
2656
2657  if (begin_time <= first_recorded_time_)
2658    db_->GetStartDate(&first_recorded_time_);
2659
2660  request->ForwardResult();
2661
2662  if (history_publisher_.get() && restrict_urls.empty())
2663    history_publisher_->DeleteUserHistoryBetween(begin_time, end_time);
2664}
2665
2666void HistoryBackend::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
2667  if (!db_.get())
2668    return;
2669
2670  for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
2671    URLRow url_row;
2672    if (!db_->GetRowForURL(*i, &url_row))
2673      continue;  // The URL isn't in the db; nothing to do.
2674
2675    VisitVector visits;
2676    db_->GetVisitsForURL(url_row.id(), &visits);
2677
2678    if (visits.empty())
2679      expirer_.DeleteURL(*i);  // There are no more visits; nuke the URL.
2680  }
2681}
2682
2683void HistoryBackend::KillHistoryDatabase() {
2684  if (!db_.get())
2685    return;
2686
2687  // Rollback transaction because Raze() cannot be called from within a
2688  // transaction.
2689  db_->RollbackTransaction();
2690  bool success = db_->Raze();
2691  UMA_HISTOGRAM_BOOLEAN("History.KillHistoryDatabaseResult", success);
2692
2693#if defined(OS_ANDROID)
2694  // Release AndroidProviderBackend before other objects.
2695  android_provider_backend_.reset();
2696#endif
2697
2698  // The expirer keeps tabs on the active databases. Tell it about the
2699  // databases which will be closed.
2700  expirer_.SetDatabases(NULL, NULL, NULL, NULL);
2701
2702  // Reopen a new transaction for |db_| for the sake of CloseAllDatabases().
2703  db_->BeginTransaction();
2704  CloseAllDatabases();
2705}
2706
2707void HistoryBackend::ProcessDBTask(
2708    scoped_refptr<HistoryDBTaskRequest> request) {
2709  DCHECK(request.get());
2710  if (request->canceled())
2711    return;
2712
2713  bool task_scheduled = !db_task_requests_.empty();
2714  // Make sure we up the refcount of the request. ProcessDBTaskImpl will
2715  // release when done with the task.
2716  request->AddRef();
2717  db_task_requests_.push_back(request.get());
2718  if (!task_scheduled) {
2719    // No other tasks are scheduled. Process request now.
2720    ProcessDBTaskImpl();
2721  }
2722}
2723
2724void HistoryBackend::BroadcastNotifications(
2725    int type,
2726    HistoryDetails* details_deleted) {
2727  // |delegate_| may be NULL if |this| is in the process of closing (closed by
2728  // HistoryService -> HistroyBackend::Closing().
2729  if (delegate_.get())
2730    delegate_->BroadcastNotifications(type, details_deleted);
2731  else
2732    delete details_deleted;
2733}
2734
2735// Deleting --------------------------------------------------------------------
2736
2737void HistoryBackend::DeleteAllHistory() {
2738  // Our approach to deleting all history is:
2739  //  1. Copy the bookmarks and their dependencies to new tables with temporary
2740  //     names.
2741  //  2. Delete the original tables. Since tables can not share pages, we know
2742  //     that any data we don't want to keep is now in an unused page.
2743  //  3. Renaming the temporary tables to match the original.
2744  //  4. Vacuuming the database to delete the unused pages.
2745  //
2746  // Since we are likely to have very few bookmarks and their dependencies
2747  // compared to all history, this is also much faster than just deleting from
2748  // the original tables directly.
2749
2750  // Get the bookmarked URLs.
2751  std::vector<BookmarkService::URLAndTitle> starred_urls;
2752  BookmarkService* bookmark_service = GetBookmarkService();
2753  if (bookmark_service)
2754    bookmark_service_->GetBookmarks(&starred_urls);
2755
2756  URLRows kept_urls;
2757  for (size_t i = 0; i < starred_urls.size(); i++) {
2758    URLRow row;
2759    if (!db_->GetRowForURL(starred_urls[i].url, &row))
2760      continue;
2761
2762    // Clear the last visit time so when we write these rows they are "clean."
2763    row.set_last_visit(Time());
2764    row.set_visit_count(0);
2765    row.set_typed_count(0);
2766    kept_urls.push_back(row);
2767  }
2768
2769  // Clear thumbnail and favicon history. The favicons for the given URLs will
2770  // be kept.
2771  if (!ClearAllThumbnailHistory(&kept_urls)) {
2772    LOG(ERROR) << "Thumbnail history could not be cleared";
2773    // We continue in this error case. If the user wants to delete their
2774    // history, we should delete as much as we can.
2775  }
2776
2777  // ClearAllMainHistory will change the IDs of the URLs in kept_urls. Therfore,
2778  // we clear the list afterwards to make sure nobody uses this invalid data.
2779  if (!ClearAllMainHistory(kept_urls))
2780    LOG(ERROR) << "Main history could not be cleared";
2781  kept_urls.clear();
2782
2783  // Delete FTS files & archived history.
2784  if (text_database_.get()) {
2785    // We assume that the text database has one transaction on them that we need
2786    // to close & restart (the long-running history transaction).
2787    text_database_->CommitTransaction();
2788    text_database_->DeleteAll();
2789    text_database_->BeginTransaction();
2790  }
2791
2792  if (archived_db_.get()) {
2793    // Close the database and delete the file.
2794    archived_db_.reset();
2795    FilePath archived_file_name = GetArchivedFileName();
2796    file_util::Delete(archived_file_name, false);
2797
2798    // Now re-initialize the database (which may fail).
2799    archived_db_.reset(new ArchivedDatabase());
2800    if (!archived_db_->Init(archived_file_name)) {
2801      LOG(WARNING) << "Could not initialize the archived database.";
2802      archived_db_.reset();
2803    } else {
2804      // Open our long-running transaction on this database.
2805      archived_db_->BeginTransaction();
2806    }
2807  }
2808
2809  db_->GetStartDate(&first_recorded_time_);
2810
2811  // Send out the notfication that history is cleared. The in-memory datdabase
2812  // will pick this up and clear itself.
2813  URLsDeletedDetails* details = new URLsDeletedDetails;
2814  details->all_history = true;
2815  BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED, details);
2816}
2817
2818bool HistoryBackend::ClearAllThumbnailHistory(URLRows* kept_urls) {
2819  if (!thumbnail_db_.get()) {
2820    // When we have no reference to the thumbnail database, maybe there was an
2821    // error opening it. In this case, we just try to blow it away to try to
2822    // fix the error if it exists. This may fail, in which case either the
2823    // file doesn't exist or there's no more we can do.
2824    file_util::Delete(GetThumbnailFileName(), false);
2825    return true;
2826  }
2827
2828  // Create duplicate icon_mapping, favicon, and favicon_bitmaps tables, this
2829  // is where the favicons we want to keep will be stored.
2830  if (!thumbnail_db_->InitTemporaryTables())
2831    return false;
2832
2833  // This maps existing favicon IDs to the ones in the temporary table.
2834  typedef std::map<FaviconID, FaviconID> FaviconMap;
2835  FaviconMap copied_favicons;
2836
2837  // Copy all unique favicons to the temporary table, and update all the
2838  // URLs to have the new IDs.
2839  for (URLRows::iterator i = kept_urls->begin(); i != kept_urls->end(); ++i) {
2840    std::vector<IconMapping> icon_mappings;
2841    if (!thumbnail_db_->GetIconMappingsForPageURL(i->url(), &icon_mappings))
2842      continue;
2843
2844    for (std::vector<IconMapping>::iterator m = icon_mappings.begin();
2845         m != icon_mappings.end(); ++m) {
2846      FaviconID old_id = m->icon_id;
2847      FaviconID new_id;
2848      FaviconMap::const_iterator found = copied_favicons.find(old_id);
2849      if (found == copied_favicons.end()) {
2850        new_id = thumbnail_db_->CopyFaviconAndFaviconBitmapsToTemporaryTables(
2851            old_id);
2852        copied_favicons[old_id] = new_id;
2853      } else {
2854        // We already encountered a URL that used this favicon, use the ID we
2855        // previously got.
2856        new_id = found->second;
2857      }
2858      // Add Icon mapping, and we don't care wheteher it suceeded or not.
2859      thumbnail_db_->AddToTemporaryIconMappingTable(i->url(), new_id);
2860    }
2861  }
2862#if defined(OS_ANDROID)
2863  // TODO (michaelbai): Add the unit test once AndroidProviderBackend is
2864  // avaliable in HistoryBackend.
2865  db_->ClearAndroidURLRows();
2866#endif
2867
2868  // Drop original favicon_bitmaps, favicons, and icon mapping tables and
2869  // replace them with the duplicate tables. Recreate the other tables. This
2870  // will make the database consistent again.
2871  thumbnail_db_->CommitTemporaryTables();
2872
2873  thumbnail_db_->RecreateThumbnailTable();
2874
2875  // Vacuum to remove all the pages associated with the dropped tables. There
2876  // must be no transaction open on the table when we do this. We assume that
2877  // our long-running transaction is open, so we complete it and start it again.
2878  DCHECK(thumbnail_db_->transaction_nesting() == 1);
2879  thumbnail_db_->CommitTransaction();
2880  thumbnail_db_->Vacuum();
2881  thumbnail_db_->BeginTransaction();
2882  return true;
2883}
2884
2885bool HistoryBackend::ClearAllMainHistory(const URLRows& kept_urls) {
2886  // Create the duplicate URL table. We will copy the kept URLs into this.
2887  if (!db_->CreateTemporaryURLTable())
2888    return false;
2889
2890  // Insert the URLs into the temporary table, we need to keep a map of changed
2891  // IDs since the ID will be different in the new table.
2892  typedef std::map<URLID, URLID> URLIDMap;
2893  URLIDMap old_to_new;  // Maps original ID to new one.
2894  for (URLRows::const_iterator i = kept_urls.begin(); i != kept_urls.end();
2895       ++i) {
2896    URLID new_id = db_->AddTemporaryURL(*i);
2897    old_to_new[i->id()] = new_id;
2898  }
2899
2900  // Replace the original URL table with the temporary one.
2901  if (!db_->CommitTemporaryURLTable())
2902    return false;
2903
2904  // Delete the old tables and recreate them empty.
2905  db_->RecreateAllTablesButURL();
2906
2907  // Vacuum to reclaim the space from the dropped tables. This must be done
2908  // when there is no transaction open, and we assume that our long-running
2909  // transaction is currently open.
2910  db_->CommitTransaction();
2911  db_->Vacuum();
2912  db_->BeginTransaction();
2913  db_->GetStartDate(&first_recorded_time_);
2914
2915  return true;
2916}
2917
2918BookmarkService* HistoryBackend::GetBookmarkService() {
2919  if (bookmark_service_)
2920    bookmark_service_->BlockTillLoaded();
2921  return bookmark_service_;
2922}
2923
2924void HistoryBackend::NotifyVisitObservers(const VisitRow& visit) {
2925  BriefVisitInfo info;
2926  info.url_id = visit.url_id;
2927  info.time = visit.visit_time;
2928  info.transition = visit.transition;
2929  // If we don't have a delegate yet during setup or shutdown, we will drop
2930  // these notifications.
2931  if (delegate_.get())
2932    delegate_->NotifyVisitDBObserversOnAddVisit(info);
2933}
2934
2935}  // namespace history
2936