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/in_memory_history_backend.h"
      6 
      7 #include <set>
      8 #include <vector>
      9 
     10 #include "base/command_line.h"
     11 #include "base/time.h"
     12 #include "base/utf_string_conversions.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/history/history_notifications.h"
     15 #include "chrome/browser/history/in_memory_database.h"
     16 #include "chrome/browser/history/in_memory_url_index.h"
     17 #include "chrome/browser/history/url_database.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "content/common/notification_details.h"
     21 #include "content/common/notification_source.h"
     22 
     23 namespace history {
     24 
     25 // If a page becomes starred we use this id in place of the real starred id.
     26 // See note in OnURLsStarred.
     27 static const StarID kBogusStarredID = 0x0FFFFFFF;
     28 
     29 InMemoryHistoryBackend::InMemoryHistoryBackend()
     30     : profile_(NULL) {
     31 }
     32 
     33 InMemoryHistoryBackend::~InMemoryHistoryBackend() {
     34   if (index_.get())
     35     index_->ShutDown();
     36 }
     37 
     38 bool InMemoryHistoryBackend::Init(const FilePath& history_filename,
     39                                   const FilePath& history_dir,
     40                                   URLDatabase* db,
     41                                   const std::string& languages) {
     42   db_.reset(new InMemoryDatabase);
     43   bool success = db_->InitFromDisk(history_filename);
     44   if (CommandLine::ForCurrentProcess()->HasSwitch(
     45           switches::kEnableHistoryQuickProvider) &&
     46       !CommandLine::ForCurrentProcess()->HasSwitch(
     47           switches::kDisableHistoryQuickProvider)) {
     48     index_.reset(new InMemoryURLIndex(history_dir));
     49 
     50     index_->Init(db, languages);
     51   }
     52   return success;
     53 }
     54 
     55 void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
     56   if (!db_.get()) {
     57     NOTREACHED();
     58     return;
     59   }
     60 
     61   profile_ = profile;
     62 
     63   // TODO(evanm): this is currently necessitated by generate_profile, which
     64   // runs without a browser process. generate_profile should really create
     65   // a browser process, at which point this check can then be nuked.
     66   if (!g_browser_process)
     67     return;
     68 
     69   // Register for the notifications we care about.
     70   // We only want notifications for the associated profile.
     71   Source<Profile> source(profile_);
     72   registrar_.Add(this, NotificationType::HISTORY_URL_VISITED, source);
     73   registrar_.Add(this, NotificationType::HISTORY_TYPED_URLS_MODIFIED, source);
     74   registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, source);
     75   registrar_.Add(this, NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
     76                  source);
     77   registrar_.Add(this, NotificationType::TEMPLATE_URL_REMOVED, source);
     78 }
     79 
     80 void InMemoryHistoryBackend::Observe(NotificationType type,
     81                                      const NotificationSource& source,
     82                                      const NotificationDetails& details) {
     83   switch (type.value) {
     84     case NotificationType::HISTORY_URL_VISITED: {
     85       Details<history::URLVisitedDetails> visited_details(details);
     86       PageTransition::Type primary_type =
     87           PageTransition::StripQualifier(visited_details->transition);
     88       if (visited_details->row.typed_count() > 0 ||
     89           primary_type == PageTransition::KEYWORD ||
     90           HasKeyword(visited_details->row.url())) {
     91         URLsModifiedDetails modified_details;
     92         modified_details.changed_urls.push_back(visited_details->row);
     93         OnTypedURLsModified(modified_details);
     94       }
     95       break;
     96     }
     97     case NotificationType::HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
     98       OnKeywordSearchTermUpdated(
     99           *Details<history::KeywordSearchTermDetails>(details).ptr());
    100       break;
    101     case NotificationType::HISTORY_TYPED_URLS_MODIFIED:
    102       OnTypedURLsModified(
    103           *Details<history::URLsModifiedDetails>(details).ptr());
    104       break;
    105     case NotificationType::HISTORY_URLS_DELETED:
    106       OnURLsDeleted(*Details<history::URLsDeletedDetails>(details).ptr());
    107       break;
    108     case NotificationType::TEMPLATE_URL_REMOVED:
    109       db_->DeleteAllSearchTermsForKeyword(
    110           *(Details<TemplateURLID>(details).ptr()));
    111       break;
    112     default:
    113       // For simplicity, the unit tests send us all notifications, even when
    114       // we haven't registered for them, so don't assert here.
    115       break;
    116   }
    117 }
    118 
    119 void InMemoryHistoryBackend::OnTypedURLsModified(
    120     const URLsModifiedDetails& details) {
    121   DCHECK(db_.get());
    122 
    123   // Add or update the URLs.
    124   //
    125   // TODO(brettw) currently the rows in the in-memory database don't match the
    126   // IDs in the main database. This sucks. Instead of Add and Remove, we should
    127   // have Sync(), which would take the ID if it's given and add it.
    128   std::vector<history::URLRow>::const_iterator i;
    129   for (i = details.changed_urls.begin();
    130        i != details.changed_urls.end(); i++) {
    131     URLID id = db_->GetRowForURL(i->url(), NULL);
    132     if (id)
    133       db_->UpdateURLRow(id, *i);
    134     else
    135       id = db_->AddURL(*i);
    136     if (index_.get())
    137       index_->UpdateURL(id, *i);
    138   }
    139 }
    140 
    141 void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
    142   DCHECK(db_.get());
    143 
    144   if (details.all_history) {
    145     // When all history is deleted, the individual URLs won't be listed. Just
    146     // create a new database to quickly clear everything out.
    147     db_.reset(new InMemoryDatabase);
    148     if (!db_->InitFromScratch())
    149       db_.reset();
    150     if (index_.get())
    151       index_->ReloadFromHistory(db_.get(), true);
    152     return;
    153   }
    154 
    155   // Delete all matching URLs in our database.
    156   for (std::set<GURL>::const_iterator i = details.urls.begin();
    157        i != details.urls.end(); ++i) {
    158     URLID id = db_->GetRowForURL(*i, NULL);
    159     if (id) {
    160       // We typically won't have most of them since we only have a subset of
    161       // history, so ignore errors.
    162       db_->DeleteURLRow(id);
    163       if (index_.get())
    164         index_->DeleteURL(id);
    165     }
    166   }
    167 }
    168 
    169 void InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
    170     const KeywordSearchTermDetails& details) {
    171   // The url won't exist for new search terms (as the user hasn't typed it), so
    172   // we force it to be added. If we end up adding a URL it won't be
    173   // autocompleted as the typed count is 0.
    174   URLRow url_row;
    175   URLID url_id;
    176   if (!db_->GetRowForURL(details.url, &url_row)) {
    177     // Because this row won't have a typed count the title and other stuff
    178     // doesn't matter. If the user ends up typing the url we'll update the title
    179     // in OnTypedURLsModified.
    180     URLRow new_row(details.url);
    181     new_row.set_last_visit(base::Time::Now());
    182     url_id = db_->AddURL(new_row);
    183     if (!url_id)
    184       return;  // Error adding.
    185   } else {
    186     url_id = url_row.id();
    187   }
    188 
    189   db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
    190 }
    191 
    192 bool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
    193   URLID id = db_->GetRowForURL(url, NULL);
    194   if (!id)
    195     return false;
    196 
    197   return db_->GetKeywordSearchTermRow(id, NULL);
    198 }
    199 
    200 }  // namespace history
    201