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 // The history system runs on a background thread so that potentially slow
      6 // database operations don't delay the browser. This backend processing is
      7 // represented by HistoryBackend. The HistoryService's job is to dispatch to
      8 // that thread.
      9 //
     10 // Main thread                       History thread
     11 // -----------                       --------------
     12 // HistoryService <----------------> HistoryBackend
     13 //                                   -> HistoryDatabase
     14 //                                      -> SQLite connection to History
     15 //                                   -> ArchivedDatabase
     16 //                                      -> SQLite connection to Archived History
     17 //                                   -> TextDatabaseManager
     18 //                                      -> SQLite connection to one month's data
     19 //                                      -> SQLite connection to one month's data
     20 //                                      ...
     21 //                                   -> ThumbnailDatabase
     22 //                                      -> SQLite connection to Thumbnails
     23 //                                         (and favicons)
     24 
     25 #include "chrome/browser/history/history.h"
     26 
     27 #include "base/callback.h"
     28 #include "base/memory/ref_counted.h"
     29 #include "base/message_loop.h"
     30 #include "base/path_service.h"
     31 #include "base/string_util.h"
     32 #include "base/task.h"
     33 #include "chrome/browser/autocomplete/history_url_provider.h"
     34 #include "chrome/browser/browser_process.h"
     35 #include "chrome/browser/history/download_create_info.h"
     36 #include "chrome/browser/history/history_backend.h"
     37 #include "chrome/browser/history/history_notifications.h"
     38 #include "chrome/browser/history/history_types.h"
     39 #include "chrome/browser/history/in_memory_database.h"
     40 #include "chrome/browser/history/in_memory_history_backend.h"
     41 #include "chrome/browser/history/top_sites.h"
     42 #include "chrome/browser/prefs/pref_service.h"
     43 #include "chrome/browser/profiles/profile.h"
     44 #include "chrome/browser/ui/profile_error_dialog.h"
     45 #include "chrome/browser/visitedlink/visitedlink_master.h"
     46 #include "chrome/common/chrome_constants.h"
     47 #include "chrome/common/pref_names.h"
     48 #include "chrome/common/thumbnail_score.h"
     49 #include "chrome/common/url_constants.h"
     50 #include "content/browser/browser_thread.h"
     51 #include "content/common/notification_service.h"
     52 #include "grit/chromium_strings.h"
     53 #include "grit/generated_resources.h"
     54 #include "third_party/skia/include/core/SkBitmap.h"
     55 
     56 using base::Time;
     57 using history::HistoryBackend;
     58 
     59 namespace {
     60 
     61 static const char* kHistoryThreadName = "Chrome_HistoryThread";
     62 
     63 }  // namespace
     64 
     65 // Sends messages from the backend to us on the main thread. This must be a
     66 // separate class from the history service so that it can hold a reference to
     67 // the history service (otherwise we would have to manually AddRef and
     68 // Release when the Backend has a reference to us).
     69 class HistoryService::BackendDelegate : public HistoryBackend::Delegate {
     70  public:
     71   explicit BackendDelegate(HistoryService* history_service)
     72       : history_service_(history_service),
     73         message_loop_(MessageLoop::current()) {
     74   }
     75 
     76   virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {
     77     // Send to the history service on the main thread.
     78     message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
     79         &HistoryService::NotifyProfileError, init_status));
     80   }
     81 
     82   virtual void SetInMemoryBackend(
     83       history::InMemoryHistoryBackend* backend) OVERRIDE {
     84     // Send the backend to the history service on the main thread.
     85     message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
     86         &HistoryService::SetInMemoryBackend, backend));
     87   }
     88 
     89   virtual void BroadcastNotifications(
     90       NotificationType type,
     91       history::HistoryDetails* details) OVERRIDE {
     92     // Send the notification on the history thread.
     93     if (NotificationService::current()) {
     94       Details<history::HistoryDetails> det(details);
     95       NotificationService::current()->Notify(type,
     96                                              NotificationService::AllSources(),
     97                                              det);
     98     }
     99     // Send the notification to the history service on the main thread.
    100     message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
    101         &HistoryService::BroadcastNotifications, type, details));
    102   }
    103 
    104   virtual void DBLoaded() OVERRIDE {
    105     message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
    106         &HistoryService::OnDBLoaded));
    107   }
    108 
    109   virtual void StartTopSitesMigration() OVERRIDE {
    110     message_loop_->PostTask(FROM_HERE, NewRunnableMethod(history_service_.get(),
    111         &HistoryService::StartTopSitesMigration));
    112   }
    113 
    114  private:
    115   scoped_refptr<HistoryService> history_service_;
    116   MessageLoop* message_loop_;
    117 };
    118 
    119 // static
    120 const history::StarID HistoryService::kBookmarkBarID = 1;
    121 
    122 // The history thread is intentionally not a BrowserThread because the
    123 // sync integration unit tests depend on being able to create more than one
    124 // history thread.
    125 HistoryService::HistoryService()
    126     : thread_(new base::Thread(kHistoryThreadName)),
    127       profile_(NULL),
    128       backend_loaded_(false),
    129       bookmark_service_(NULL),
    130       no_db_(false),
    131       needs_top_sites_migration_(false) {
    132 }
    133 
    134 HistoryService::HistoryService(Profile* profile)
    135     : thread_(new base::Thread(kHistoryThreadName)),
    136       profile_(profile),
    137       backend_loaded_(false),
    138       bookmark_service_(NULL),
    139       no_db_(false),
    140       needs_top_sites_migration_(false) {
    141   DCHECK(profile_);
    142   registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED,
    143                  Source<Profile>(profile_));
    144   registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED,
    145                  Source<Profile>(profile_));
    146 }
    147 
    148 HistoryService::~HistoryService() {
    149   // Shutdown the backend. This does nothing if Cleanup was already invoked.
    150   Cleanup();
    151 }
    152 
    153 bool HistoryService::BackendLoaded() {
    154   // NOTE: We start the backend loading even though it completes asynchronously
    155   // and thus won't affect the return value of this function.  This is because
    156   // callers of this assume that if the backend isn't yet loaded it will be
    157   // soon, so they will either listen for notifications or just retry this call
    158   // later.  If we've purged the backend, we haven't necessarily restarted it
    159   // loading by now, so we need to trigger the load in order to maintain that
    160   // expectation.
    161   LoadBackendIfNecessary();
    162   return backend_loaded_;
    163 }
    164 
    165 void HistoryService::UnloadBackend() {
    166   if (!history_backend_)
    167     return;  // Already unloaded.
    168 
    169   // Get rid of the in-memory backend.
    170   in_memory_backend_.reset();
    171 
    172   // The backend's destructor must run on the history thread since it is not
    173   // threadsafe. So this thread must not be the last thread holding a reference
    174   // to the backend, or a crash could happen.
    175   //
    176   // We have a reference to the history backend. There is also an extra
    177   // reference held by our delegate installed in the backend, which
    178   // HistoryBackend::Closing will release. This means if we scheduled a call
    179   // to HistoryBackend::Closing and *then* released our backend reference, there
    180   // will be a race between us and the backend's Closing function to see who is
    181   // the last holder of a reference. If the backend thread's Closing manages to
    182   // run before we release our backend refptr, the last reference will be held
    183   // by this thread and the destructor will be called from here.
    184   //
    185   // Therefore, we create a task to run the Closing operation first. This holds
    186   // a reference to the backend. Then we release our reference, then we schedule
    187   // the task to run. After the task runs, it will delete its reference from
    188   // the history thread, ensuring everything works properly.
    189   Task* closing_task =
    190       NewRunnableMethod(history_backend_.get(), &HistoryBackend::Closing);
    191   history_backend_ = NULL;
    192   ScheduleTask(PRIORITY_NORMAL, closing_task);
    193 }
    194 
    195 void HistoryService::Cleanup() {
    196   if (!thread_) {
    197     // We've already cleaned up.
    198     return;
    199   }
    200 
    201   // Unload the backend.
    202   UnloadBackend();
    203 
    204   // Delete the thread, which joins with the background thread. We defensively
    205   // NULL the pointer before deleting it in case somebody tries to use it
    206   // during shutdown, but this shouldn't happen.
    207   base::Thread* thread = thread_;
    208   thread_ = NULL;
    209   delete thread;
    210 }
    211 
    212 void HistoryService::NotifyRenderProcessHostDestruction(const void* host) {
    213   ScheduleAndForget(PRIORITY_NORMAL,
    214                     &HistoryBackend::NotifyRenderProcessHostDestruction, host);
    215 }
    216 
    217 history::URLDatabase* HistoryService::InMemoryDatabase() {
    218   // NOTE: See comments in BackendLoaded() as to why we call
    219   // LoadBackendIfNecessary() here even though it won't affect the return value
    220   // for this call.
    221   LoadBackendIfNecessary();
    222   if (in_memory_backend_.get())
    223     return in_memory_backend_->db();
    224   return NULL;
    225 }
    226 
    227 history::InMemoryURLIndex* HistoryService::InMemoryIndex() {
    228   // NOTE: See comments in BackendLoaded() as to why we call
    229   // LoadBackendIfNecessary() here even though it won't affect the return value
    230   // for this call.
    231   LoadBackendIfNecessary();
    232   if (in_memory_backend_.get())
    233     return in_memory_backend_->InMemoryIndex();
    234   return NULL;
    235 }
    236 
    237 void HistoryService::SetSegmentPresentationIndex(int64 segment_id, int index) {
    238   ScheduleAndForget(PRIORITY_UI,
    239                     &HistoryBackend::SetSegmentPresentationIndex,
    240                     segment_id, index);
    241 }
    242 
    243 void HistoryService::SetKeywordSearchTermsForURL(const GURL& url,
    244                                                  TemplateURLID keyword_id,
    245                                                  const string16& term) {
    246   ScheduleAndForget(PRIORITY_UI,
    247                     &HistoryBackend::SetKeywordSearchTermsForURL,
    248                     url, keyword_id, term);
    249 }
    250 
    251 void HistoryService::DeleteAllSearchTermsForKeyword(
    252     TemplateURLID keyword_id) {
    253   ScheduleAndForget(PRIORITY_UI,
    254                     &HistoryBackend::DeleteAllSearchTermsForKeyword,
    255                     keyword_id);
    256 }
    257 
    258 HistoryService::Handle HistoryService::GetMostRecentKeywordSearchTerms(
    259     TemplateURLID keyword_id,
    260     const string16& prefix,
    261     int max_count,
    262     CancelableRequestConsumerBase* consumer,
    263     GetMostRecentKeywordSearchTermsCallback* callback) {
    264   return Schedule(PRIORITY_UI, &HistoryBackend::GetMostRecentKeywordSearchTerms,
    265                   consumer,
    266                   new history::GetMostRecentKeywordSearchTermsRequest(callback),
    267                   keyword_id, prefix, max_count);
    268 }
    269 
    270 void HistoryService::URLsNoLongerBookmarked(const std::set<GURL>& urls) {
    271   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::URLsNoLongerBookmarked,
    272                     urls);
    273 }
    274 
    275 HistoryService::Handle HistoryService::ScheduleDBTask(
    276     HistoryDBTask* task,
    277     CancelableRequestConsumerBase* consumer) {
    278   history::HistoryDBTaskRequest* request = new history::HistoryDBTaskRequest(
    279       NewCallback(task, &HistoryDBTask::DoneRunOnMainThread));
    280   request->value = task;  // The value is the task to execute.
    281   return Schedule(PRIORITY_UI, &HistoryBackend::ProcessDBTask, consumer,
    282                   request);
    283 }
    284 
    285 HistoryService::Handle HistoryService::QuerySegmentUsageSince(
    286     CancelableRequestConsumerBase* consumer,
    287     const Time from_time,
    288     int max_result_count,
    289     SegmentQueryCallback* callback) {
    290   return Schedule(PRIORITY_UI, &HistoryBackend::QuerySegmentUsage,
    291                   consumer, new history::QuerySegmentUsageRequest(callback),
    292                   from_time, max_result_count);
    293 }
    294 
    295 void HistoryService::SetOnBackendDestroyTask(Task* task) {
    296   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetOnBackendDestroyTask,
    297                     MessageLoop::current(), task);
    298 }
    299 
    300 void HistoryService::AddPage(const GURL& url,
    301                              const void* id_scope,
    302                              int32 page_id,
    303                              const GURL& referrer,
    304                              PageTransition::Type transition,
    305                              const history::RedirectList& redirects,
    306                              history::VisitSource visit_source,
    307                              bool did_replace_entry) {
    308   AddPage(url, Time::Now(), id_scope, page_id, referrer, transition, redirects,
    309           visit_source, did_replace_entry);
    310 }
    311 
    312 void HistoryService::AddPage(const GURL& url,
    313                              Time time,
    314                              const void* id_scope,
    315                              int32 page_id,
    316                              const GURL& referrer,
    317                              PageTransition::Type transition,
    318                              const history::RedirectList& redirects,
    319                              history::VisitSource visit_source,
    320                              bool did_replace_entry) {
    321   scoped_refptr<history::HistoryAddPageArgs> request(
    322       new history::HistoryAddPageArgs(url, time, id_scope, page_id, referrer,
    323                                       redirects, transition, visit_source,
    324                                       did_replace_entry));
    325   AddPage(*request);
    326 }
    327 
    328 void HistoryService::AddPage(const history::HistoryAddPageArgs& add_page_args) {
    329   DCHECK(thread_) << "History service being called after cleanup";
    330 
    331   // Filter out unwanted URLs. We don't add auto-subframe URLs. They are a
    332   // large part of history (think iframes for ads) and we never display them in
    333   // history UI. We will still add manual subframes, which are ones the user
    334   // has clicked on to get.
    335   if (!CanAddURL(add_page_args.url))
    336     return;
    337 
    338   // Add link & all redirects to visited link list.
    339   VisitedLinkMaster* visited_links;
    340   if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) {
    341     visited_links->AddURL(add_page_args.url);
    342 
    343     if (!add_page_args.redirects.empty()) {
    344       // We should not be asked to add a page in the middle of a redirect chain.
    345       DCHECK_EQ(add_page_args.url,
    346                 add_page_args.redirects[add_page_args.redirects.size() - 1]);
    347 
    348       // We need the !redirects.empty() condition above since size_t is unsigned
    349       // and will wrap around when we subtract one from a 0 size.
    350       for (size_t i = 0; i < add_page_args.redirects.size() - 1; i++)
    351         visited_links->AddURL(add_page_args.redirects[i]);
    352     }
    353   }
    354 
    355   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::AddPage,
    356                     scoped_refptr<history::HistoryAddPageArgs>(
    357                         add_page_args.Clone()));
    358 }
    359 
    360 void HistoryService::AddPageNoVisitForBookmark(const GURL& url) {
    361   if (!CanAddURL(url))
    362     return;
    363 
    364   ScheduleAndForget(PRIORITY_NORMAL,
    365                     &HistoryBackend::AddPageNoVisitForBookmark, url);
    366 }
    367 
    368 void HistoryService::SetPageTitle(const GURL& url,
    369                                   const string16& title) {
    370   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageTitle, url, title);
    371 }
    372 
    373 void HistoryService::AddPageWithDetails(const GURL& url,
    374                                         const string16& title,
    375                                         int visit_count,
    376                                         int typed_count,
    377                                         Time last_visit,
    378                                         bool hidden,
    379                                         history::VisitSource visit_source) {
    380   // Filter out unwanted URLs.
    381   if (!CanAddURL(url))
    382     return;
    383 
    384   // Add to the visited links system.
    385   VisitedLinkMaster* visited_links;
    386   if (profile_ && (visited_links = profile_->GetVisitedLinkMaster()))
    387     visited_links->AddURL(url);
    388 
    389   history::URLRow row(url);
    390   row.set_title(title);
    391   row.set_visit_count(visit_count);
    392   row.set_typed_count(typed_count);
    393   row.set_last_visit(last_visit);
    394   row.set_hidden(hidden);
    395 
    396   std::vector<history::URLRow> rows;
    397   rows.push_back(row);
    398 
    399   ScheduleAndForget(PRIORITY_NORMAL,
    400                     &HistoryBackend::AddPagesWithDetails, rows, visit_source);
    401 }
    402 
    403 void HistoryService::AddPagesWithDetails(
    404     const std::vector<history::URLRow>& info,
    405     history::VisitSource visit_source) {
    406 
    407   // Add to the visited links system.
    408   VisitedLinkMaster* visited_links;
    409   if (profile_ && (visited_links = profile_->GetVisitedLinkMaster())) {
    410     std::vector<GURL> urls;
    411     urls.reserve(info.size());
    412     for (std::vector<history::URLRow>::const_iterator i = info.begin();
    413          i != info.end();
    414          ++i)
    415       urls.push_back(i->url());
    416 
    417     visited_links->AddURLs(urls);
    418   }
    419 
    420   ScheduleAndForget(PRIORITY_NORMAL,
    421                     &HistoryBackend::AddPagesWithDetails, info, visit_source);
    422 }
    423 
    424 void HistoryService::SetPageContents(const GURL& url,
    425                                      const string16& contents) {
    426   if (!CanAddURL(url))
    427     return;
    428 
    429   ScheduleAndForget(PRIORITY_LOW, &HistoryBackend::SetPageContents,
    430                     url, contents);
    431 }
    432 
    433 void HistoryService::SetPageThumbnail(const GURL& page_url,
    434                                       const SkBitmap& thumbnail,
    435                                       const ThumbnailScore& score) {
    436   if (!CanAddURL(page_url))
    437     return;
    438 
    439   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetPageThumbnail,
    440                     page_url, thumbnail, score);
    441 }
    442 
    443 HistoryService::Handle HistoryService::GetPageThumbnail(
    444     const GURL& page_url,
    445     CancelableRequestConsumerBase* consumer,
    446     ThumbnailDataCallback* callback) {
    447   return Schedule(PRIORITY_NORMAL, &HistoryBackend::GetPageThumbnail, consumer,
    448                   new history::GetPageThumbnailRequest(callback), page_url);
    449 }
    450 
    451 void HistoryService::GetFavicon(FaviconService::GetFaviconRequest* request,
    452                                 const GURL& icon_url,
    453                                 history::IconType icon_type) {
    454   Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFavicon, NULL, request,
    455            icon_url, icon_type);
    456 }
    457 
    458 void HistoryService::UpdateFaviconMappingAndFetch(
    459     FaviconService::GetFaviconRequest* request,
    460     const GURL& page_url,
    461     const GURL& icon_url,
    462     history::IconType icon_type) {
    463   Schedule(PRIORITY_NORMAL, &HistoryBackend::UpdateFaviconMappingAndFetch, NULL,
    464            request, page_url, icon_url, history::FAVICON);
    465 }
    466 
    467 void HistoryService::GetFaviconForURL(
    468     FaviconService::GetFaviconRequest* request,
    469     const GURL& page_url,
    470     int icon_types) {
    471   Schedule(PRIORITY_NORMAL, &HistoryBackend::GetFaviconForURL, NULL, request,
    472            page_url, icon_types);
    473 }
    474 
    475 void HistoryService::SetFavicon(const GURL& page_url,
    476                                 const GURL& icon_url,
    477                                 const std::vector<unsigned char>& image_data,
    478                                 history::IconType icon_type) {
    479   if (!CanAddURL(page_url))
    480     return;
    481 
    482   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::SetFavicon,
    483       page_url, icon_url,
    484       scoped_refptr<RefCountedMemory>(new RefCountedBytes(image_data)),
    485       icon_type);
    486 }
    487 
    488 void HistoryService::SetFaviconOutOfDateForPage(const GURL& page_url) {
    489   ScheduleAndForget(PRIORITY_NORMAL,
    490                     &HistoryBackend::SetFaviconOutOfDateForPage, page_url);
    491 }
    492 
    493 void HistoryService::SetImportedFavicons(
    494     const std::vector<history::ImportedFaviconUsage>& favicon_usage) {
    495   ScheduleAndForget(PRIORITY_NORMAL,
    496                     &HistoryBackend::SetImportedFavicons, favicon_usage);
    497 }
    498 
    499 void HistoryService::IterateURLs(URLEnumerator* enumerator) {
    500   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator);
    501 }
    502 
    503 HistoryService::Handle HistoryService::QueryURL(
    504     const GURL& url,
    505     bool want_visits,
    506     CancelableRequestConsumerBase* consumer,
    507     QueryURLCallback* callback) {
    508   return Schedule(PRIORITY_UI, &HistoryBackend::QueryURL, consumer,
    509                   new history::QueryURLRequest(callback), url, want_visits);
    510 }
    511 
    512 // Downloads -------------------------------------------------------------------
    513 
    514 // Handle creation of a download by creating an entry in the history service's
    515 // 'downloads' table.
    516 HistoryService::Handle HistoryService::CreateDownload(
    517     const DownloadCreateInfo& create_info,
    518     CancelableRequestConsumerBase* consumer,
    519     HistoryService::DownloadCreateCallback* callback) {
    520   return Schedule(PRIORITY_NORMAL, &HistoryBackend::CreateDownload, consumer,
    521                   new history::DownloadCreateRequest(callback), create_info);
    522 }
    523 
    524 // Handle queries for a list of all downloads in the history database's
    525 // 'downloads' table.
    526 HistoryService::Handle HistoryService::QueryDownloads(
    527     CancelableRequestConsumerBase* consumer,
    528     DownloadQueryCallback* callback) {
    529   return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryDownloads, consumer,
    530                   new history::DownloadQueryRequest(callback));
    531 }
    532 
    533 // Changes all IN_PROGRESS in the database entries to CANCELED.
    534 // IN_PROGRESS entries are the corrupted entries, not updated by next function
    535 // because of the crash or some other extremal exit.
    536 void HistoryService::CleanUpInProgressEntries() {
    537   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::CleanUpInProgressEntries);
    538 }
    539 
    540 // Handle updates for a particular download. This is a 'fire and forget'
    541 // operation, so we don't need to be called back.
    542 void HistoryService::UpdateDownload(int64 received_bytes,
    543                                     int32 state,
    544                                     int64 db_handle) {
    545   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownload,
    546                     received_bytes, state, db_handle);
    547 }
    548 
    549 void HistoryService::UpdateDownloadPath(const FilePath& path,
    550                                         int64 db_handle) {
    551   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::UpdateDownloadPath,
    552                     path, db_handle);
    553 }
    554 
    555 void HistoryService::RemoveDownload(int64 db_handle) {
    556   ScheduleAndForget(PRIORITY_NORMAL,
    557                     &HistoryBackend::RemoveDownload, db_handle);
    558 }
    559 
    560 void HistoryService::RemoveDownloadsBetween(Time remove_begin,
    561                                             Time remove_end) {
    562   ScheduleAndForget(PRIORITY_NORMAL,
    563                     &HistoryBackend::RemoveDownloadsBetween,
    564                     remove_begin,
    565                     remove_end);
    566 }
    567 
    568 HistoryService::Handle HistoryService::QueryHistory(
    569     const string16& text_query,
    570     const history::QueryOptions& options,
    571     CancelableRequestConsumerBase* consumer,
    572     QueryHistoryCallback* callback) {
    573   return Schedule(PRIORITY_UI, &HistoryBackend::QueryHistory, consumer,
    574                   new history::QueryHistoryRequest(callback),
    575                   text_query, options);
    576 }
    577 
    578 HistoryService::Handle HistoryService::QueryRedirectsFrom(
    579     const GURL& from_url,
    580     CancelableRequestConsumerBase* consumer,
    581     QueryRedirectsCallback* callback) {
    582   return Schedule(PRIORITY_UI, &HistoryBackend::QueryRedirectsFrom, consumer,
    583       new history::QueryRedirectsRequest(callback), from_url);
    584 }
    585 
    586 HistoryService::Handle HistoryService::QueryRedirectsTo(
    587     const GURL& to_url,
    588     CancelableRequestConsumerBase* consumer,
    589     QueryRedirectsCallback* callback) {
    590   return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryRedirectsTo, consumer,
    591       new history::QueryRedirectsRequest(callback), to_url);
    592 }
    593 
    594 HistoryService::Handle HistoryService::GetVisitCountToHost(
    595     const GURL& url,
    596     CancelableRequestConsumerBase* consumer,
    597     GetVisitCountToHostCallback* callback) {
    598   return Schedule(PRIORITY_UI, &HistoryBackend::GetVisitCountToHost, consumer,
    599       new history::GetVisitCountToHostRequest(callback), url);
    600 }
    601 
    602 HistoryService::Handle HistoryService::QueryTopURLsAndRedirects(
    603     int result_count,
    604     CancelableRequestConsumerBase* consumer,
    605     QueryTopURLsAndRedirectsCallback* callback) {
    606   return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryTopURLsAndRedirects,
    607       consumer, new history::QueryTopURLsAndRedirectsRequest(callback),
    608       result_count);
    609 }
    610 
    611 HistoryService::Handle HistoryService::QueryMostVisitedURLs(
    612     int result_count,
    613     int days_back,
    614     CancelableRequestConsumerBase* consumer,
    615     QueryMostVisitedURLsCallback* callback) {
    616   return Schedule(PRIORITY_NORMAL, &HistoryBackend::QueryMostVisitedURLs,
    617                   consumer,
    618                   new history::QueryMostVisitedURLsRequest(callback),
    619                   result_count, days_back);
    620 }
    621 
    622 void HistoryService::Observe(NotificationType type,
    623                              const NotificationSource& source,
    624                              const NotificationDetails& details) {
    625   if (!thread_)
    626     return;
    627 
    628   switch (type.value) {
    629     case NotificationType::HISTORY_URLS_DELETED: {
    630       // Update the visited link system for deleted URLs. We will update the
    631       // visited link system for added URLs as soon as we get the add
    632       // notification (we don't have to wait for the backend, which allows us to
    633       // be faster to update the state).
    634       //
    635       // For deleted URLs, we don't typically know what will be deleted since
    636       // delete notifications are by time. We would also like to be more
    637       // respectful of privacy and never tell the user something is gone when it
    638       // isn't. Therefore, we update the delete URLs after the fact.
    639       if (!profile_)
    640         return;  // No profile, probably unit testing.
    641       Details<history::URLsDeletedDetails> deleted_details(details);
    642       VisitedLinkMaster* visited_links = profile_->GetVisitedLinkMaster();
    643       if (!visited_links)
    644         return;  // Nobody to update.
    645       if (deleted_details->all_history)
    646         visited_links->DeleteAllURLs();
    647       else  // Delete individual ones.
    648         visited_links->DeleteURLs(deleted_details->urls);
    649       break;
    650     }
    651 
    652     case NotificationType::TEMPLATE_URL_REMOVED:
    653       DeleteAllSearchTermsForKeyword(*(Details<TemplateURLID>(details).ptr()));
    654       break;
    655 
    656     default:
    657       NOTREACHED();
    658   }
    659 }
    660 
    661 bool HistoryService::Init(const FilePath& history_dir,
    662                           BookmarkService* bookmark_service,
    663                           bool no_db) {
    664   if (!thread_->Start()) {
    665     Cleanup();
    666     return false;
    667   }
    668 
    669   history_dir_ = history_dir;
    670   bookmark_service_ = bookmark_service;
    671   no_db_ = no_db;
    672 
    673   // Create the history backend.
    674   LoadBackendIfNecessary();
    675   return true;
    676 }
    677 
    678 void HistoryService::ScheduleAutocomplete(HistoryURLProvider* provider,
    679                                           HistoryURLProviderParams* params) {
    680   ScheduleAndForget(PRIORITY_UI, &HistoryBackend::ScheduleAutocomplete,
    681                     scoped_refptr<HistoryURLProvider>(provider), params);
    682 }
    683 
    684 void HistoryService::ScheduleTask(SchedulePriority priority,
    685                                   Task* task) {
    686   // TODO(brettw): do prioritization.
    687   thread_->message_loop()->PostTask(FROM_HERE, task);
    688 }
    689 
    690 // static
    691 bool HistoryService::CanAddURL(const GURL& url) {
    692   if (!url.is_valid())
    693     return false;
    694 
    695   // TODO: We should allow kChromeUIScheme URLs if they have been explicitly
    696   // typed.  Right now, however, these are marked as typed even when triggered
    697   // by a shortcut or menu action.
    698   if (url.SchemeIs(chrome::kJavaScriptScheme) ||
    699       url.SchemeIs(chrome::kChromeDevToolsScheme) ||
    700       url.SchemeIs(chrome::kChromeUIScheme) ||
    701       url.SchemeIs(chrome::kViewSourceScheme) ||
    702       url.SchemeIs(chrome::kChromeInternalScheme))
    703     return false;
    704 
    705   if (url.SchemeIs(chrome::kAboutScheme)) {
    706     if (LowerCaseEqualsASCII(url.path(), "blank"))
    707       return false;
    708     // We allow all other about URLs since the user may like to see things
    709     // like "about:memory" or "about:histograms" in their history and
    710     // autocomplete.
    711   }
    712 
    713   return true;
    714 }
    715 
    716 void HistoryService::SetInMemoryBackend(
    717     history::InMemoryHistoryBackend* mem_backend) {
    718   DCHECK(!in_memory_backend_.get()) << "Setting mem DB twice";
    719   in_memory_backend_.reset(mem_backend);
    720 
    721   // The database requires additional initialization once we own it.
    722   in_memory_backend_->AttachToHistoryService(profile_);
    723 }
    724 
    725 void HistoryService::NotifyProfileError(sql::InitStatus init_status) {
    726   ShowProfileErrorDialog(
    727       (init_status == sql::INIT_FAILURE) ?
    728       IDS_COULDNT_OPEN_PROFILE_ERROR : IDS_PROFILE_TOO_NEW_ERROR);
    729 }
    730 
    731 void HistoryService::DeleteURL(const GURL& url) {
    732   // We will update the visited links when we observe the delete notifications.
    733   ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteURL, url);
    734 }
    735 
    736 void HistoryService::ExpireHistoryBetween(
    737     const std::set<GURL>& restrict_urls,
    738     Time begin_time, Time end_time,
    739     CancelableRequestConsumerBase* consumer,
    740     ExpireHistoryCallback* callback) {
    741 
    742   // We will update the visited links when we observe the delete notifications.
    743   Schedule(PRIORITY_UI, &HistoryBackend::ExpireHistoryBetween, consumer,
    744            new history::ExpireHistoryRequest(callback),
    745            restrict_urls, begin_time, end_time);
    746 }
    747 
    748 void HistoryService::BroadcastNotifications(
    749     NotificationType type,
    750     history::HistoryDetails* details_deleted) {
    751   // We take ownership of the passed-in pointer and delete it. It was made for
    752   // us on another thread, so the caller doesn't know when we will handle it.
    753   scoped_ptr<history::HistoryDetails> details(details_deleted);
    754   // TODO(evanm): this is currently necessitated by generate_profile, which
    755   // runs without a browser process. generate_profile should really create
    756   // a browser process, at which point this check can then be nuked.
    757   if (!g_browser_process)
    758     return;
    759 
    760   if (!thread_)
    761     return;
    762 
    763   // The source of all of our notifications is the profile. Note that this
    764   // pointer is NULL in unit tests.
    765   Source<Profile> source(profile_);
    766 
    767   // The details object just contains the pointer to the object that the
    768   // backend has allocated for us. The receiver of the notification will cast
    769   // this to the proper type.
    770   Details<history::HistoryDetails> det(details_deleted);
    771 
    772   NotificationService::current()->Notify(type, source, det);
    773 }
    774 
    775 void HistoryService::LoadBackendIfNecessary() {
    776   if (!thread_ || history_backend_)
    777     return;  // Failed to init, or already started loading.
    778 
    779   scoped_refptr<HistoryBackend> backend(
    780       new HistoryBackend(history_dir_,
    781                          new BackendDelegate(this),
    782                          bookmark_service_));
    783   history_backend_.swap(backend);
    784 
    785   // There may not be a profile when unit testing.
    786   std::string languages;
    787   if (profile_) {
    788     PrefService* prefs = profile_->GetPrefs();
    789     languages = prefs->GetString(prefs::kAcceptLanguages);
    790   }
    791   ScheduleAndForget(PRIORITY_UI, &HistoryBackend::Init, languages, no_db_);
    792 }
    793 
    794 void HistoryService::OnDBLoaded() {
    795   backend_loaded_ = true;
    796   NotificationService::current()->Notify(NotificationType::HISTORY_LOADED,
    797                                          Source<Profile>(profile_),
    798                                          Details<HistoryService>(this));
    799   if (thread_ && profile_) {
    800     // We don't want to force creation of TopSites.
    801     history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
    802     if (ts)
    803       ts->HistoryLoaded();
    804   }
    805 }
    806 
    807 void HistoryService::StartTopSitesMigration() {
    808   needs_top_sites_migration_ = true;
    809   if (thread_ && profile_) {
    810     // We don't want to force creation of TopSites.
    811     history::TopSites* ts = profile_->GetTopSitesWithoutCreating();
    812     if (ts)
    813       ts->MigrateFromHistory();
    814   }
    815 }
    816 
    817 void HistoryService::OnTopSitesReady() {
    818   ScheduleAndForget(PRIORITY_NORMAL,
    819                     &HistoryBackend::MigrateThumbnailsDatabase);
    820 }
    821