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