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.
5 #include "chrome/browser/history/in_memory_history_backend.h"
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"
24 InMemoryHistoryBackend::InMemoryHistoryBackend()
28 InMemoryHistoryBackend::~InMemoryHistoryBackend() {}
30 bool InMemoryHistoryBackend::Init(const base::FilePath& history_filename,
32 db_.reset(new InMemoryDatabase);
33 return db_->InitFromDisk(history_filename);
36 void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) {
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)
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,
56 registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED, source);
58 this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, source);
60 this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED, source);
61 registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, source);
64 void InMemoryHistoryBackend::Observe(
66 const content::NotificationSource& source,
67 const content::NotificationDetails& details) {
69 case chrome::NOTIFICATION_HISTORY_URL_VISITED: {
70 content::Details<history::URLVisitedDetails> visited_details(details);
71 content::PageTransition primary_type =
72 content::PageTransitionStripQualifier(visited_details->transition);
73 if (visited_details->row.typed_count() > 0 ||
74 primary_type == content::PAGE_TRANSITION_KEYWORD ||
75 HasKeyword(visited_details->row.url())) {
76 URLsModifiedDetails modified_details;
77 modified_details.changed_urls.push_back(visited_details->row);
78 OnTypedURLsModified(modified_details);
82 case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
83 OnKeywordSearchTermUpdated(
84 *content::Details<history::KeywordSearchUpdatedDetails>(
87 case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED:
88 OnKeywordSearchTermDeleted(
89 *content::Details<history::KeywordSearchDeletedDetails>(
92 case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED:
94 *content::Details<history::URLsModifiedDetails>(details).ptr());
96 case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
98 *content::Details<history::URLsDeletedDetails>(details).ptr());
100 case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED:
101 db_->DeleteAllSearchTermsForKeyword(
102 *(content::Details<TemplateURLID>(details).ptr()));
105 // For simplicity, the unit tests send us all notifications, even when
106 // we haven't registered for them, so don't assert here.
111 void InMemoryHistoryBackend::OnTypedURLsModified(
112 const URLsModifiedDetails& details) {
115 // Add or update the URLs.
117 // TODO(brettw) currently the rows in the in-memory database don't match the
118 // IDs in the main database. This sucks. Instead of Add and Remove, we should
119 // have Sync(), which would take the ID if it's given and add it.
120 URLRows::const_iterator i;
121 for (i = details.changed_urls.begin();
122 i != details.changed_urls.end(); ++i) {
123 if (i->typed_count() > 0) {
124 URLID id = db_->GetRowForURL(i->url(), NULL);
126 db_->UpdateURLRow(id, *i);
133 void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
136 if (details.all_history) {
137 // When all history is deleted, the individual URLs won't be listed. Just
138 // create a new database to quickly clear everything out.
139 db_.reset(new InMemoryDatabase);
140 if (!db_->InitFromScratch())
145 // Delete all matching URLs in our database.
146 for (URLRows::const_iterator row = details.rows.begin();
147 row != details.rows.end(); ++row) {
148 // We typically won't have most of them since we only have a subset of
149 // history, so ignore errors.
150 db_->DeleteURLRow(row->id());
154 void InMemoryHistoryBackend::OnKeywordSearchTermUpdated(
155 const KeywordSearchUpdatedDetails& details) {
156 // The url won't exist for new search terms (as the user hasn't typed it), so
157 // we force it to be added. If we end up adding a URL it won't be
158 // autocompleted as the typed count is 0.
161 if (!db_->GetRowForURL(details.url, &url_row)) {
162 // Because this row won't have a typed count the title and other stuff
163 // doesn't matter. If the user ends up typing the url we'll update the title
164 // in OnTypedURLsModified.
165 URLRow new_row(details.url);
166 new_row.set_last_visit(base::Time::Now());
167 url_id = db_->AddURL(new_row);
169 return; // Error adding.
171 url_id = url_row.id();
174 db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
177 void InMemoryHistoryBackend::OnKeywordSearchTermDeleted(
178 const KeywordSearchDeletedDetails& details) {
179 URLID url_id = db_->GetRowForURL(details.url, NULL);
181 db_->DeleteKeywordSearchTermForURL(url_id);
184 bool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
185 URLID id = db_->GetRowForURL(url, NULL);
189 return db_->GetKeywordSearchTermRow(id, NULL);
192 } // namespace history