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