Home | History | Annotate | Download | only in history
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/history/in_memory_history_backend.h"
      6 
      7 #include <set>
      8 #include <vector>
      9 
     10 #include "base/command_line.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "base/time/time.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/chrome_notification_types.h"
     15 #include "chrome/browser/history/history_notifications.h"
     16 #include "chrome/browser/history/in_memory_database.h"
     17 #include "chrome/browser/history/url_database.h"
     18 #include "chrome/browser/profiles/profile.h"
     19 #include "content/public/browser/notification_details.h"
     20 #include "content/public/browser/notification_source.h"
     21 
     22 namespace history {
     23 
     24 InMemoryHistoryBackend::InMemoryHistoryBackend()
     25     : profile_(NULL) {
     26 }
     27 
     28 InMemoryHistoryBackend::~InMemoryHistoryBackend() {}
     29 
     30 bool InMemoryHistoryBackend::Init(const base::FilePath& history_filename,
     31                                   URLDatabase* db) {
     32   db_.reset(new InMemoryDatabase);
     33   return db_->InitFromDisk(history_filename);
     34 }
     35 
     36 void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
     37   if (!db_) {
     38     NOTREACHED();
     39     return;
     40   }
     41 
     42   profile_ = profile;
     43 
     44   // TODO(evanm): this is currently necessitated by generate_profile, which
     45   // runs without a browser process. generate_profile should really create
     46   // a browser process, at which point this check can then be nuked.
     47   if (!g_browser_process)
     48     return;
     49 
     50   // Register for the notifications we care about.
     51   // We only want notifications for the associated profile.
     52   content::Source<Profile> source(profile_);
     53   registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, source);
     54   registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
     55                  source);
     56   registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source);
     57   registrar_.Add(this,
     58                  chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED,
     59                  source);
     60   registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, source);
     61 }
     62 
     63 void InMemoryHistoryBackend::Observe(
     64     int type,
     65     const content::NotificationSource& source,
     66     const content::NotificationDetails& details) {
     67   switch (type) {
     68     case chrome::NOTIFICATION_HISTORY_URL_VISITED: {
     69       content::Details<history::URLVisitedDetails> visited_details(details);
     70       content::PageTransition primary_type =
     71           content::PageTransitionStripQualifier(visited_details->transition);
     72       if (visited_details->row.typed_count() > 0 ||
     73           primary_type == content::PAGE_TRANSITION_KEYWORD ||
     74           HasKeyword(visited_details->row.url())) {
     75         URLsModifiedDetails modified_details;
     76         modified_details.changed_urls.push_back(visited_details->row);
     77         OnTypedURLsModified(modified_details);
     78       }
     79       break;
     80     }
     81     case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
     82       OnKeywordSearchTermUpdated(
     83           *content::Details<history::KeywordSearchTermDetails>(details).ptr());
     84       break;
     85     case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED:
     86       OnTypedURLsModified(
     87           *content::Details<history::URLsModifiedDetails>(details).ptr());
     88       break;
     89     case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
     90       OnURLsDeleted(
     91           *content::Details<history::URLsDeletedDetails>(details).ptr());
     92       break;
     93     case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED:
     94       db_->DeleteAllSearchTermsForKeyword(
     95           *(content::Details<TemplateURLID>(details).ptr()));
     96       break;
     97     default:
     98       // For simplicity, the unit tests send us all notifications, even when
     99       // we haven't registered for them, so don't assert here.
    100       break;
    101   }
    102 }
    103 
    104 void InMemoryHistoryBackend::OnTypedURLsModified(
    105     const URLsModifiedDetails& details) {
    106   DCHECK(db_);
    107 
    108   // Add or update the URLs.
    109   //
    110   // TODO(brettw) currently the rows in the in-memory database don't match the
    111   // IDs in the main database. This sucks. Instead of Add and Remove, we should
    112   // have Sync(), which would take the ID if it's given and add it.
    113   URLRows::const_iterator i;
    114   for (i = details.changed_urls.begin();
    115        i != details.changed_urls.end(); ++i) {
    116     if (i->typed_count() > 0) {
    117       URLID id = db_->GetRowForURL(i->url(), NULL);
    118       if (id)
    119         db_->UpdateURLRow(id, *i);
    120       else
    121         db_->AddURL(*i);
    122     }
    123   }
    124 }
    125 
    126 void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
    127   DCHECK(db_);
    128 
    129   if (details.all_history) {
    130     // When all history is deleted, the individual URLs won't be listed. Just
    131     // create a new database to quickly clear everything out.
    132     db_.reset(new InMemoryDatabase);
    133     if (!db_->InitFromScratch())
    134       db_.reset();
    135     return;
    136   }
    137 
    138   // Delete all matching URLs in our database.
    139   for (URLRows::const_iterator row = details.rows.begin();
    140        row != details.rows.end(); ++row) {
    141     // We typically won't have most of them since we only have a subset of
    142     // history, so ignore errors.
    143     db_->DeleteURLRow(row->id());
    144   }
    145 }
    146 
    147 void InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
    148     const KeywordSearchTermDetails& details) {
    149   // The url won't exist for new search terms (as the user hasn't typed it), so
    150   // we force it to be added. If we end up adding a URL it won't be
    151   // autocompleted as the typed count is 0.
    152   URLRow url_row;
    153   URLID url_id;
    154   if (!db_->GetRowForURL(details.url, &url_row)) {
    155     // Because this row won't have a typed count the title and other stuff
    156     // doesn't matter. If the user ends up typing the url we'll update the title
    157     // in OnTypedURLsModified.
    158     URLRow new_row(details.url);
    159     new_row.set_last_visit(base::Time::Now());
    160     url_id = db_->AddURL(new_row);
    161     if (!url_id)
    162       return;  // Error adding.
    163   } else {
    164     url_id = url_row.id();
    165   }
    166 
    167   db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
    168 }
    169 
    170 bool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
    171   URLID id = db_->GetRowForURL(url, NULL);
    172   if (!id)
    173     return false;
    174 
    175   return db_->GetKeywordSearchTermRow(id, NULL);
    176 }
    177 
    178 }  // namespace history
    179