• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
InMemoryHistoryBackend()29 InMemoryHistoryBackend::InMemoryHistoryBackend()
30     : profile_(NULL) {
31 }
32 
~InMemoryHistoryBackend()33 InMemoryHistoryBackend::~InMemoryHistoryBackend() {
34   if (index_.get())
35     index_->ShutDown();
36 }
37 
Init(const FilePath & history_filename,const FilePath & history_dir,URLDatabase * db,const std::string & languages)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 
AttachToHistoryService(Profile * profile)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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
OnTypedURLsModified(const URLsModifiedDetails & details)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 
OnURLsDeleted(const URLsDeletedDetails & details)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 
OnKeywordSearchTermUpdated(const KeywordSearchTermDetails & details)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 
HasKeyword(const GURL & url)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