Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Side by Side Diff: chrome/browser/history/shortcuts_backend.cc

Issue 200493006: Move the ShortcutsBackend from history to autocomplete so that it can fully (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/shortcuts_backend.h"
6
7 #include <map>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/guid.h"
14 #include "base/i18n/case_conversion.h"
15 #include "base/strings/string_util.h"
16 #include "chrome/browser/autocomplete/autocomplete_match.h"
17 #include "chrome/browser/autocomplete/autocomplete_result.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/history/history_notifications.h"
20 #include "chrome/browser/history/history_service.h"
21 #include "chrome/browser/history/shortcuts_database.h"
22 #include "chrome/browser/omnibox/omnibox_log.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/common/chrome_constants.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_source.h"
28 #include "extensions/common/extension.h"
29
30 using content::BrowserThread;
31
32 namespace {
33
34 // Takes Match classification vector and removes all matched positions,
35 // compacting repetitions if necessary.
36 ACMatchClassifications StripMatchMarkers(
37 const ACMatchClassifications& matches) {
38 ACMatchClassifications unmatched;
39 for (ACMatchClassifications::const_iterator i(matches.begin());
40 i != matches.end(); ++i) {
41 AutocompleteMatch::AddLastClassificationIfNecessary(
42 &unmatched, i->offset, i->style & ~ACMatchClassification::MATCH);
43 }
44 return unmatched;
45 }
46
47 // Normally shortcuts have the same match type as the original match they were
48 // created from, but for certain match types, we should modify the shortcut's
49 // type slightly to reflect that the origin of the shortcut is historical.
50 AutocompleteMatch::Type GetTypeForShortcut(AutocompleteMatch::Type type) {
51 switch (type) {
52 case AutocompleteMatchType::URL_WHAT_YOU_TYPED:
53 case AutocompleteMatchType::NAVSUGGEST:
54 return AutocompleteMatchType::HISTORY_URL;
55
56 case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
57 case AutocompleteMatchType::SEARCH_SUGGEST:
58 case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY:
59 case AutocompleteMatchType::SEARCH_SUGGEST_INFINITE:
60 case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
61 case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE:
62 return AutocompleteMatchType::SEARCH_HISTORY;
63
64 default:
65 return type;
66 }
67 }
68
69 } // namespace
70
71 namespace history {
72
73 // ShortcutsBackend::Shortcut::MatchCore --------------------------------------
74
75 ShortcutsBackend::Shortcut::MatchCore::MatchCore(
76 const AutocompleteMatch& match)
77 : fill_into_edit(match.fill_into_edit),
78 destination_url(match.destination_url),
79 contents(match.contents),
80 contents_class(StripMatchMarkers(match.contents_class)),
81 description(match.description),
82 description_class(StripMatchMarkers(match.description_class)),
83 transition(match.transition),
84 type(GetTypeForShortcut(match.type)),
85 keyword(match.keyword) {
86 }
87
88 ShortcutsBackend::Shortcut::MatchCore::MatchCore(
89 const base::string16& fill_into_edit,
90 const GURL& destination_url,
91 const base::string16& contents,
92 const ACMatchClassifications& contents_class,
93 const base::string16& description,
94 const ACMatchClassifications& description_class,
95 content::PageTransition transition,
96 AutocompleteMatch::Type type,
97 const base::string16& keyword)
98 : fill_into_edit(fill_into_edit),
99 destination_url(destination_url),
100 contents(contents),
101 contents_class(StripMatchMarkers(contents_class)),
102 description(description),
103 description_class(StripMatchMarkers(description_class)),
104 transition(transition),
105 type(GetTypeForShortcut(type)),
106 keyword(keyword) {
107 }
108
109 ShortcutsBackend::Shortcut::MatchCore::~MatchCore() {
110 }
111
112 AutocompleteMatch ShortcutsBackend::Shortcut::MatchCore::ToMatch() const {
113 AutocompleteMatch match;
114 match.fill_into_edit = fill_into_edit;
115 match.destination_url = destination_url;
116 match.contents = contents;
117 match.contents_class = contents_class;
118 match.description = description;
119 match.description_class = description_class;
120 match.transition = transition;
121 match.type = type;
122 match.keyword = keyword;
123 return match;
124 }
125
126
127 // ShortcutsBackend::Shortcut -------------------------------------------------
128
129 ShortcutsBackend::Shortcut::Shortcut(
130 const std::string& id,
131 const base::string16& text,
132 const MatchCore& match_core,
133 const base::Time& last_access_time,
134 int number_of_hits)
135 : id(id),
136 text(text),
137 match_core(match_core),
138 last_access_time(last_access_time),
139 number_of_hits(number_of_hits) {
140 }
141
142 ShortcutsBackend::Shortcut::Shortcut()
143 : match_core(AutocompleteMatch()),
144 last_access_time(base::Time::Now()),
145 number_of_hits(0) {
146 }
147
148 ShortcutsBackend::Shortcut::~Shortcut() {
149 }
150
151
152 // ShortcutsBackend -----------------------------------------------------------
153
154 ShortcutsBackend::ShortcutsBackend(Profile* profile, bool suppress_db)
155 : current_state_(NOT_INITIALIZED),
156 no_db_access_(suppress_db) {
157 if (!suppress_db) {
158 db_ = new ShortcutsDatabase(
159 profile->GetPath().Append(chrome::kShortcutsDatabaseName));
160 }
161 // |profile| can be NULL in tests.
162 if (profile) {
163 notification_registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
164 content::Source<Profile>(profile));
165 notification_registrar_.Add(this, chrome::NOTIFICATION_HISTORY_URLS_DELETED,
166 content::Source<Profile>(profile));
167 }
168 }
169
170 bool ShortcutsBackend::Init() {
171 if (current_state_ != NOT_INITIALIZED)
172 return false;
173
174 if (no_db_access_) {
175 current_state_ = INITIALIZED;
176 return true;
177 }
178
179 current_state_ = INITIALIZING;
180 return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
181 base::Bind(&ShortcutsBackend::InitInternal, this));
182 }
183
184 bool ShortcutsBackend::DeleteShortcutsWithUrl(const GURL& shortcut_url) {
185 return initialized() && DeleteShortcutsWithUrl(shortcut_url, true);
186 }
187
188 void ShortcutsBackend::AddObserver(ShortcutsBackendObserver* obs) {
189 observer_list_.AddObserver(obs);
190 }
191
192 void ShortcutsBackend::RemoveObserver(ShortcutsBackendObserver* obs) {
193 observer_list_.RemoveObserver(obs);
194 }
195
196 void ShortcutsBackend::AddOrUpdateShortcut(const base::string16& text,
197 const AutocompleteMatch& match) {
198 const base::string16 text_lowercase(base::i18n::ToLower(text));
199 const base::Time now(base::Time::Now());
200 for (ShortcutMap::const_iterator it(
201 shortcuts_map_.lower_bound(text_lowercase));
202 it != shortcuts_map_.end() &&
203 StartsWith(it->first, text_lowercase, true); ++it) {
204 if (match.destination_url == it->second.match_core.destination_url) {
205 UpdateShortcut(Shortcut(it->second.id, text, Shortcut::MatchCore(match),
206 now, it->second.number_of_hits + 1));
207 return;
208 }
209 }
210 AddShortcut(Shortcut(base::GenerateGUID(), text, Shortcut::MatchCore(match),
211 now, 1));
212 }
213
214 ShortcutsBackend::~ShortcutsBackend() {
215 }
216
217 void ShortcutsBackend::ShutdownOnUIThread() {
218 DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
219 BrowserThread::CurrentlyOn(BrowserThread::UI));
220 notification_registrar_.RemoveAll();
221 }
222
223 void ShortcutsBackend::Observe(int type,
224 const content::NotificationSource& source,
225 const content::NotificationDetails& details) {
226 if (!initialized())
227 return;
228
229 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
230 // When an extension is unloaded, we want to remove any Shortcuts associated
231 // with it.
232 DeleteShortcutsWithUrl(content::Details<extensions::UnloadedExtensionInfo>(
233 details)->extension->url(), false);
234 return;
235 }
236
237 DCHECK_EQ(chrome::NOTIFICATION_HISTORY_URLS_DELETED, type);
238 const history::URLsDeletedDetails* deleted_details =
239 content::Details<const history::URLsDeletedDetails>(details).ptr();
240 if (deleted_details->all_history)
241 DeleteAllShortcuts();
242 const URLRows& rows(deleted_details->rows);
243 std::vector<std::string> shortcut_ids;
244
245 for (GuidMap::const_iterator it(guid_map_.begin()); it != guid_map_.end();
246 ++it) {
247 if (std::find_if(
248 rows.begin(), rows.end(), URLRow::URLRowHasURL(
249 it->second->second.match_core.destination_url)) != rows.end())
250 shortcut_ids.push_back(it->first);
251 }
252 DeleteShortcutsWithIds(shortcut_ids);
253 }
254
255 void ShortcutsBackend::InitInternal() {
256 DCHECK(current_state_ == INITIALIZING);
257 db_->Init();
258 ShortcutsDatabase::GuidToShortcutMap shortcuts;
259 db_->LoadShortcuts(&shortcuts);
260 temp_shortcuts_map_.reset(new ShortcutMap);
261 temp_guid_map_.reset(new GuidMap);
262 for (ShortcutsDatabase::GuidToShortcutMap::const_iterator it(
263 shortcuts.begin()); it != shortcuts.end(); ++it) {
264 (*temp_guid_map_)[it->first] = temp_shortcuts_map_->insert(
265 std::make_pair(base::i18n::ToLower(it->second.text), it->second));
266 }
267 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
268 base::Bind(&ShortcutsBackend::InitCompleted, this));
269 }
270
271 void ShortcutsBackend::InitCompleted() {
272 temp_guid_map_->swap(guid_map_);
273 temp_shortcuts_map_->swap(shortcuts_map_);
274 temp_shortcuts_map_.reset(NULL);
275 temp_guid_map_.reset(NULL);
276 current_state_ = INITIALIZED;
277 FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
278 OnShortcutsLoaded());
279 }
280
281 bool ShortcutsBackend::AddShortcut(const Shortcut& shortcut) {
282 if (!initialized())
283 return false;
284 DCHECK(guid_map_.find(shortcut.id) == guid_map_.end());
285 guid_map_[shortcut.id] = shortcuts_map_.insert(
286 std::make_pair(base::i18n::ToLower(shortcut.text), shortcut));
287 FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
288 OnShortcutsChanged());
289 return no_db_access_ || BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
290 base::Bind(base::IgnoreResult(&ShortcutsDatabase::AddShortcut),
291 db_.get(), shortcut));
292 }
293
294 bool ShortcutsBackend::UpdateShortcut(const Shortcut& shortcut) {
295 if (!initialized())
296 return false;
297 GuidMap::iterator it(guid_map_.find(shortcut.id));
298 if (it != guid_map_.end())
299 shortcuts_map_.erase(it->second);
300 guid_map_[shortcut.id] = shortcuts_map_.insert(
301 std::make_pair(base::i18n::ToLower(shortcut.text), shortcut));
302 FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
303 OnShortcutsChanged());
304 return no_db_access_ || BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
305 base::Bind(base::IgnoreResult(&ShortcutsDatabase::UpdateShortcut),
306 db_.get(), shortcut));
307 }
308
309 bool ShortcutsBackend::DeleteShortcutsWithIds(
310 const std::vector<std::string>& shortcut_ids) {
311 if (!initialized())
312 return false;
313 for (size_t i = 0; i < shortcut_ids.size(); ++i) {
314 GuidMap::iterator it(guid_map_.find(shortcut_ids[i]));
315 if (it != guid_map_.end()) {
316 shortcuts_map_.erase(it->second);
317 guid_map_.erase(it);
318 }
319 }
320 FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
321 OnShortcutsChanged());
322 return no_db_access_ || BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
323 base::Bind(base::IgnoreResult(&ShortcutsDatabase::DeleteShortcutsWithIds),
324 db_.get(), shortcut_ids));
325 }
326
327 bool ShortcutsBackend::DeleteShortcutsWithUrl(const GURL& url,
328 bool exact_match) {
329 const std::string& url_spec = url.spec();
330 std::vector<std::string> shortcut_ids;
331 for (GuidMap::iterator it(guid_map_.begin()); it != guid_map_.end(); ) {
332 if (exact_match ?
333 (it->second->second.match_core.destination_url == url) :
334 StartsWithASCII(it->second->second.match_core.destination_url.spec(),
335 url_spec, true)) {
336 shortcut_ids.push_back(it->first);
337 shortcuts_map_.erase(it->second);
338 guid_map_.erase(it++);
339 } else {
340 ++it;
341 }
342 }
343 FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
344 OnShortcutsChanged());
345 return no_db_access_ ||
346 BrowserThread::PostTask(
347 BrowserThread::DB, FROM_HERE,
348 base::Bind(
349 base::IgnoreResult(&ShortcutsDatabase::DeleteShortcutsWithUrl),
350 db_.get(), url_spec));
351 }
352
353 bool ShortcutsBackend::DeleteAllShortcuts() {
354 if (!initialized())
355 return false;
356 shortcuts_map_.clear();
357 guid_map_.clear();
358 FOR_EACH_OBSERVER(ShortcutsBackendObserver, observer_list_,
359 OnShortcutsChanged());
360 return no_db_access_ || BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
361 base::Bind(base::IgnoreResult(&ShortcutsDatabase::DeleteAllShortcuts),
362 db_.get()));
363 }
364
365 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/history/shortcuts_backend.h ('k') | chrome/browser/history/shortcuts_backend_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698