root/chrome/browser/history/in_memory_history_backend.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Init
  2. AttachToHistoryService
  3. Observe
  4. OnTypedURLsModified
  5. OnURLsDeleted
  6. OnKeywordSearchTermUpdated
  7. OnKeywordSearchTermDeleted
  8. HasKeyword

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/history/in_memory_history_backend.h"

#include <set>
#include <vector>

#include "base/command_line.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/in_memory_database.h"
#include "chrome/browser/history/url_database.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"

namespace history {

InMemoryHistoryBackend::InMemoryHistoryBackend()
    : profile_(NULL) {
}

InMemoryHistoryBackend::~InMemoryHistoryBackend() {}

bool InMemoryHistoryBackend::Init(const base::FilePath& history_filename,
                                  URLDatabase* db) {
  db_.reset(new InMemoryDatabase);
  return db_->InitFromDisk(history_filename);
}

void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
  if (!db_) {
    NOTREACHED();
    return;
  }

  profile_ = profile;

  // TODO(evanm): this is currently necessitated by generate_profile, which
  // runs without a browser process. generate_profile should really create
  // a browser process, at which point this check can then be nuked.
  if (!g_browser_process)
    return;

  // Register for the notifications we care about.
  // We only want notifications for the associated profile.
  content::Source<Profile> source(profile_);
  registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URL_VISITED, source);
  registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
                 source);
  registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source);
  registrar_.Add(
      this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, source);
  registrar_.Add(
      this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED, source);
  registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, source);
}

void InMemoryHistoryBackend::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  switch (type) {
    case chrome::NOTIFICATION_HISTORY_URL_VISITED: {
      content::Details<history::URLVisitedDetails> visited_details(details);
      content::PageTransition primary_type =
          content::PageTransitionStripQualifier(visited_details->transition);
      if (visited_details->row.typed_count() > 0 ||
          primary_type == content::PAGE_TRANSITION_KEYWORD ||
          HasKeyword(visited_details->row.url())) {
        URLsModifiedDetails modified_details;
        modified_details.changed_urls.push_back(visited_details->row);
        OnTypedURLsModified(modified_details);
      }
      break;
    }
    case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
      OnKeywordSearchTermUpdated(
          *content::Details<history::KeywordSearchUpdatedDetails>(
              details).ptr());
      break;
    case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED:
      OnKeywordSearchTermDeleted(
          *content::Details<history::KeywordSearchDeletedDetails>(
              details).ptr());
      break;
    case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED:
      OnTypedURLsModified(
          *content::Details<history::URLsModifiedDetails>(details).ptr());
      break;
    case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
      OnURLsDeleted(
          *content::Details<history::URLsDeletedDetails>(details).ptr());
      break;
    case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED:
      db_->DeleteAllSearchTermsForKeyword(
          *(content::Details<TemplateURLID>(details).ptr()));
      break;
    default:
      // For simplicity, the unit tests send us all notifications, even when
      // we haven't registered for them, so don't assert here.
      break;
  }
}

void InMemoryHistoryBackend::OnTypedURLsModified(
    const URLsModifiedDetails& details) {
  DCHECK(db_);

  // Add or update the URLs.
  //
  // TODO(brettw) currently the rows in the in-memory database don't match the
  // IDs in the main database. This sucks. Instead of Add and Remove, we should
  // have Sync(), which would take the ID if it's given and add it.
  URLRows::const_iterator i;
  for (i = details.changed_urls.begin();
       i != details.changed_urls.end(); ++i) {
    if (i->typed_count() > 0) {
      URLID id = db_->GetRowForURL(i->url(), NULL);
      if (id)
        db_->UpdateURLRow(id, *i);
      else
        db_->AddURL(*i);
    }
  }
}

void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
  DCHECK(db_);

  if (details.all_history) {
    // When all history is deleted, the individual URLs won't be listed. Just
    // create a new database to quickly clear everything out.
    db_.reset(new InMemoryDatabase);
    if (!db_->InitFromScratch())
      db_.reset();
    return;
  }

  // Delete all matching URLs in our database.
  for (URLRows::const_iterator row = details.rows.begin();
       row != details.rows.end(); ++row) {
    // We typically won't have most of them since we only have a subset of
    // history, so ignore errors.
    db_->DeleteURLRow(row->id());
  }
}

void InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
    const KeywordSearchUpdatedDetails& details) {
  // The url won't exist for new search terms (as the user hasn't typed it), so
  // we force it to be added. If we end up adding a URL it won't be
  // autocompleted as the typed count is 0.
  URLRow url_row;
  URLID url_id;
  if (!db_->GetRowForURL(details.url, &url_row)) {
    // Because this row won't have a typed count the title and other stuff
    // doesn't matter. If the user ends up typing the url we'll update the title
    // in OnTypedURLsModified.
    URLRow new_row(details.url);
    new_row.set_last_visit(base::Time::Now());
    url_id = db_->AddURL(new_row);
    if (!url_id)
      return;  // Error adding.
  } else {
    url_id = url_row.id();
  }

  db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
}

void InMemoryHistoryBackend::OnKeywordSearchTermDeleted(
    const KeywordSearchDeletedDetails& details) {
  URLID url_id = db_->GetRowForURL(details.url, NULL);
  if (url_id)
    db_->DeleteKeywordSearchTermForURL(url_id);
}

bool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
  URLID id = db_->GetRowForURL(url, NULL);
  if (!id)
    return false;

  return db_->GetKeywordSearchTermRow(id, NULL);
}

}  // namespace history

/* [<][>][^][v][top][bottom][index][help] */