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