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