- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / in_memory_history_backend.cc
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(
58       this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED, source);
59   registrar_.Add(
60       this, chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED, source);
61   registrar_.Add(this, chrome::NOTIFICATION_TEMPLATE_URL_REMOVED, source);
62 }
63
64 void InMemoryHistoryBackend::Observe(
65     int type,
66     const content::NotificationSource& source,
67     const content::NotificationDetails& details) {
68   switch (type) {
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);
79       }
80       break;
81     }
82     case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_UPDATED:
83       OnKeywordSearchTermUpdated(
84           *content::Details<history::KeywordSearchUpdatedDetails>(
85               details).ptr());
86       break;
87     case chrome::NOTIFICATION_HISTORY_KEYWORD_SEARCH_TERM_DELETED:
88       OnKeywordSearchTermDeleted(
89           *content::Details<history::KeywordSearchDeletedDetails>(
90               details).ptr());
91       break;
92     case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED:
93       OnTypedURLsModified(
94           *content::Details<history::URLsModifiedDetails>(details).ptr());
95       break;
96     case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
97       OnURLsDeleted(
98           *content::Details<history::URLsDeletedDetails>(details).ptr());
99       break;
100     case chrome::NOTIFICATION_TEMPLATE_URL_REMOVED:
101       db_->DeleteAllSearchTermsForKeyword(
102           *(content::Details<TemplateURLID>(details).ptr()));
103       break;
104     default:
105       // For simplicity, the unit tests send us all notifications, even when
106       // we haven't registered for them, so don't assert here.
107       break;
108   }
109 }
110
111 void InMemoryHistoryBackend::OnTypedURLsModified(
112     const URLsModifiedDetails& details) {
113   DCHECK(db_);
114
115   // Add or update the URLs.
116   //
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);
125       if (id)
126         db_->UpdateURLRow(id, *i);
127       else
128         db_->AddURL(*i);
129     }
130   }
131 }
132
133 void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) {
134   DCHECK(db_);
135
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())
141       db_.reset();
142     return;
143   }
144
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());
151   }
152 }
153
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.
159   URLRow url_row;
160   URLID url_id;
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);
168     if (!url_id)
169       return;  // Error adding.
170   } else {
171     url_id = url_row.id();
172   }
173
174   db_->SetKeywordSearchTermsForURL(url_id, details.keyword_id, details.term);
175 }
176
177 void InMemoryHistoryBackend::OnKeywordSearchTermDeleted(
178     const KeywordSearchDeletedDetails& details) {
179   URLID url_id = db_->GetRowForURL(details.url, NULL);
180   if (url_id)
181     db_->DeleteKeywordSearchTermForURL(url_id);
182 }
183
184 bool InMemoryHistoryBackend::HasKeyword(const GURL& url) {
185   URLID id = db_->GetRowForURL(url, NULL);
186   if (!id)
187     return false;
188
189   return db_->GetKeywordSearchTermRow(id, NULL);
190 }
191
192 }  // namespace history