OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/autocomplete/shortcuts_provider.h" | 5 #include "chrome/browser/autocomplete/shortcuts_provider.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <map> | 9 #include <map> |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include "base/i18n/break_iterator.h" | 12 #include "base/i18n/break_iterator.h" |
13 #include "base/i18n/case_conversion.h" | 13 #include "base/i18n/case_conversion.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
16 #include "base/prefs/pref_service.h" | 16 #include "base/prefs/pref_service.h" |
17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
19 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
20 #include "base/time/time.h" | 20 #include "base/time/time.h" |
21 #include "chrome/browser/autocomplete/autocomplete_input.h" | 21 #include "chrome/browser/autocomplete/autocomplete_input.h" |
22 #include "chrome/browser/autocomplete/autocomplete_match.h" | 22 #include "chrome/browser/autocomplete/autocomplete_match.h" |
23 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" | 23 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" |
24 #include "chrome/browser/autocomplete/autocomplete_result.h" | 24 #include "chrome/browser/autocomplete/autocomplete_result.h" |
25 #include "chrome/browser/autocomplete/history_provider.h" | 25 #include "chrome/browser/autocomplete/history_provider.h" |
| 26 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h" |
26 #include "chrome/browser/autocomplete/url_prefix.h" | 27 #include "chrome/browser/autocomplete/url_prefix.h" |
27 #include "chrome/browser/history/history_notifications.h" | 28 #include "chrome/browser/history/history_notifications.h" |
28 #include "chrome/browser/history/history_service.h" | 29 #include "chrome/browser/history/history_service.h" |
29 #include "chrome/browser/history/history_service_factory.h" | 30 #include "chrome/browser/history/history_service_factory.h" |
30 #include "chrome/browser/history/shortcuts_backend_factory.h" | |
31 #include "chrome/browser/omnibox/omnibox_field_trial.h" | 31 #include "chrome/browser/omnibox/omnibox_field_trial.h" |
32 #include "chrome/browser/profiles/profile.h" | 32 #include "chrome/browser/profiles/profile.h" |
33 #include "chrome/common/net/url_fixer_upper.h" | 33 #include "chrome/common/net/url_fixer_upper.h" |
34 #include "chrome/common/pref_names.h" | 34 #include "chrome/common/pref_names.h" |
35 #include "chrome/common/url_constants.h" | 35 #include "chrome/common/url_constants.h" |
36 #include "url/url_parse.h" | 36 #include "url/url_parse.h" |
37 | 37 |
38 namespace { | 38 namespace { |
39 | 39 |
40 class DestinationURLEqualsURL { | 40 class DestinationURLEqualsURL { |
(...skipping 28 matching lines...) Expand all Loading... |
69 } | 69 } |
70 | 70 |
71 } // namespace | 71 } // namespace |
72 | 72 |
73 ShortcutsProvider::ShortcutsProvider(AutocompleteProviderListener* listener, | 73 ShortcutsProvider::ShortcutsProvider(AutocompleteProviderListener* listener, |
74 Profile* profile) | 74 Profile* profile) |
75 : AutocompleteProvider(listener, profile, | 75 : AutocompleteProvider(listener, profile, |
76 AutocompleteProvider::TYPE_SHORTCUTS), | 76 AutocompleteProvider::TYPE_SHORTCUTS), |
77 languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)), | 77 languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)), |
78 initialized_(false) { | 78 initialized_(false) { |
79 scoped_refptr<history::ShortcutsBackend> backend = | 79 scoped_refptr<ShortcutsBackend> backend = |
80 ShortcutsBackendFactory::GetForProfile(profile_); | 80 ShortcutsBackendFactory::GetForProfile(profile_); |
81 if (backend.get()) { | 81 if (backend.get()) { |
82 backend->AddObserver(this); | 82 backend->AddObserver(this); |
83 if (backend->initialized()) | 83 if (backend->initialized()) |
84 initialized_ = true; | 84 initialized_ = true; |
85 } | 85 } |
86 } | 86 } |
87 | 87 |
88 void ShortcutsProvider::Start(const AutocompleteInput& input, | 88 void ShortcutsProvider::Start(const AutocompleteInput& input, |
89 bool minimal_changes) { | 89 bool minimal_changes) { |
(...skipping 18 matching lines...) Expand all Loading... |
108 base::HistogramBase* counter = base::Histogram::FactoryGet( | 108 base::HistogramBase* counter = base::Histogram::FactoryGet( |
109 name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag); | 109 name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag); |
110 counter->Add(static_cast<int>((end_time - start_time).InMilliseconds())); | 110 counter->Add(static_cast<int>((end_time - start_time).InMilliseconds())); |
111 } | 111 } |
112 UpdateStarredStateOfMatches(); | 112 UpdateStarredStateOfMatches(); |
113 } | 113 } |
114 | 114 |
115 void ShortcutsProvider::DeleteMatch(const AutocompleteMatch& match) { | 115 void ShortcutsProvider::DeleteMatch(const AutocompleteMatch& match) { |
116 // Copy the URL since deleting from |matches_| will invalidate |match|. | 116 // Copy the URL since deleting from |matches_| will invalidate |match|. |
117 GURL url(match.destination_url); | 117 GURL url(match.destination_url); |
| 118 DCHECK(url.is_valid()); |
118 | 119 |
119 // When a user deletes a match, he probably means for the URL to disappear out | 120 // When a user deletes a match, he probably means for the URL to disappear out |
120 // of history entirely. So nuke all shortcuts that map to this URL. | 121 // of history entirely. So nuke all shortcuts that map to this URL. |
121 scoped_refptr<history::ShortcutsBackend> backend = | 122 scoped_refptr<ShortcutsBackend> backend = |
122 ShortcutsBackendFactory::GetForProfileIfExists(profile_); | 123 ShortcutsBackendFactory::GetForProfileIfExists(profile_); |
123 if (backend) // Can be NULL in Incognito. | 124 if (backend) // Can be NULL in Incognito. |
124 backend->DeleteShortcutsWithUrl(url); | 125 backend->DeleteShortcutsWithURL(url); |
125 | 126 |
126 matches_.erase(std::remove_if(matches_.begin(), matches_.end(), | 127 matches_.erase(std::remove_if(matches_.begin(), matches_.end(), |
127 DestinationURLEqualsURL(url)), | 128 DestinationURLEqualsURL(url)), |
128 matches_.end()); | 129 matches_.end()); |
129 // NOTE: |match| is now dead! | 130 // NOTE: |match| is now dead! |
130 | 131 |
131 // Delete the match from the history DB. This will eventually result in a | 132 // Delete the match from the history DB. This will eventually result in a |
132 // second call to DeleteShortcutsWithURLs(), which is harmless. | 133 // second call to DeleteShortcutsWithURL(), which is harmless. |
133 HistoryService* const history_service = | 134 HistoryService* const history_service = |
134 HistoryServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS); | 135 HistoryServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS); |
135 | 136 DCHECK(history_service); |
136 DCHECK(history_service && url.is_valid()); | |
137 history_service->DeleteURL(url); | 137 history_service->DeleteURL(url); |
138 } | 138 } |
139 | 139 |
140 ShortcutsProvider::~ShortcutsProvider() { | 140 ShortcutsProvider::~ShortcutsProvider() { |
141 scoped_refptr<history::ShortcutsBackend> backend = | 141 scoped_refptr<ShortcutsBackend> backend = |
142 ShortcutsBackendFactory::GetForProfileIfExists(profile_); | 142 ShortcutsBackendFactory::GetForProfileIfExists(profile_); |
143 if (backend.get()) | 143 if (backend.get()) |
144 backend->RemoveObserver(this); | 144 backend->RemoveObserver(this); |
145 } | 145 } |
146 | 146 |
147 void ShortcutsProvider::OnShortcutsLoaded() { | 147 void ShortcutsProvider::OnShortcutsLoaded() { |
148 initialized_ = true; | 148 initialized_ = true; |
149 } | 149 } |
150 | 150 |
151 void ShortcutsProvider::GetMatches(const AutocompleteInput& input) { | 151 void ShortcutsProvider::GetMatches(const AutocompleteInput& input) { |
152 scoped_refptr<history::ShortcutsBackend> backend = | 152 scoped_refptr<ShortcutsBackend> backend = |
153 ShortcutsBackendFactory::GetForProfileIfExists(profile_); | 153 ShortcutsBackendFactory::GetForProfileIfExists(profile_); |
154 if (!backend.get()) | 154 if (!backend.get()) |
155 return; | 155 return; |
156 // Get the URLs from the shortcuts database with keys that partially or | 156 // Get the URLs from the shortcuts database with keys that partially or |
157 // completely match the search term. | 157 // completely match the search term. |
158 base::string16 term_string(base::i18n::ToLower(input.text())); | 158 base::string16 term_string(base::i18n::ToLower(input.text())); |
159 DCHECK(!term_string.empty()); | 159 DCHECK(!term_string.empty()); |
160 | 160 |
161 base::string16 fixed_up_term_string(term_string); | 161 base::string16 fixed_up_term_string(term_string); |
162 AutocompleteInput fixed_up_input(input); | 162 AutocompleteInput fixed_up_input(input); |
163 if (FixupUserInput(&fixed_up_input)) | 163 if (FixupUserInput(&fixed_up_input)) |
164 fixed_up_term_string = fixed_up_input.text(); | 164 fixed_up_term_string = fixed_up_input.text(); |
165 | 165 |
166 int max_relevance; | 166 int max_relevance; |
167 if (!OmniboxFieldTrial::ShortcutsScoringMaxRelevance( | 167 if (!OmniboxFieldTrial::ShortcutsScoringMaxRelevance( |
168 input.current_page_classification(), &max_relevance)) | 168 input.current_page_classification(), &max_relevance)) |
169 max_relevance = AutocompleteResult::kLowestDefaultScore - 1; | 169 max_relevance = AutocompleteResult::kLowestDefaultScore - 1; |
170 | 170 |
171 for (history::ShortcutsBackend::ShortcutMap::const_iterator it = | 171 for (ShortcutsBackend::ShortcutMap::const_iterator it = |
172 FindFirstMatch(term_string, backend.get()); | 172 FindFirstMatch(term_string, backend.get()); |
173 it != backend->shortcuts_map().end() && | 173 it != backend->shortcuts_map().end() && |
174 StartsWith(it->first, term_string, true); ++it) { | 174 StartsWith(it->first, term_string, true); ++it) { |
175 // Don't return shortcuts with zero relevance. | 175 // Don't return shortcuts with zero relevance. |
176 int relevance = CalculateScore(term_string, it->second, max_relevance); | 176 int relevance = CalculateScore(term_string, it->second, max_relevance); |
177 if (relevance) { | 177 if (relevance) { |
178 matches_.push_back(ShortcutToACMatch( | 178 matches_.push_back(ShortcutToACMatch( |
179 it->second, relevance, term_string, fixed_up_term_string, | 179 it->second, relevance, term_string, fixed_up_term_string, |
180 input.prevent_inline_autocomplete())); | 180 input.prevent_inline_autocomplete())); |
181 matches_.back().ComputeStrippedDestinationURL(profile_); | 181 matches_.back().ComputeStrippedDestinationURL(profile_); |
(...skipping 28 matching lines...) Expand all Loading... |
210 } | 210 } |
211 for (ACMatches::iterator it = matches_.begin(); it != matches_.end(); ++it) { | 211 for (ACMatches::iterator it = matches_.begin(); it != matches_.end(); ++it) { |
212 max_relevance = std::min(max_relevance, it->relevance); | 212 max_relevance = std::min(max_relevance, it->relevance); |
213 it->relevance = max_relevance; | 213 it->relevance = max_relevance; |
214 if (max_relevance > 1) | 214 if (max_relevance > 1) |
215 --max_relevance; | 215 --max_relevance; |
216 } | 216 } |
217 } | 217 } |
218 | 218 |
219 AutocompleteMatch ShortcutsProvider::ShortcutToACMatch( | 219 AutocompleteMatch ShortcutsProvider::ShortcutToACMatch( |
220 const history::ShortcutsBackend::Shortcut& shortcut, | 220 const history::ShortcutsDatabase::Shortcut& shortcut, |
221 int relevance, | 221 int relevance, |
222 const base::string16& term_string, | 222 const base::string16& term_string, |
223 const base::string16& fixed_up_term_string, | 223 const base::string16& fixed_up_term_string, |
224 const bool prevent_inline_autocomplete) { | 224 const bool prevent_inline_autocomplete) { |
225 DCHECK(!term_string.empty()); | 225 DCHECK(!term_string.empty()); |
226 AutocompleteMatch match(shortcut.match_core.ToMatch()); | 226 AutocompleteMatch match; |
227 match.provider = this; | 227 match.provider = this; |
228 match.relevance = relevance; | 228 match.relevance = relevance; |
229 match.deletable = true; | 229 match.deletable = true; |
| 230 match.fill_into_edit = shortcut.match_core.fill_into_edit; |
| 231 match.destination_url = shortcut.match_core.destination_url; |
230 DCHECK(match.destination_url.is_valid()); | 232 DCHECK(match.destination_url.is_valid()); |
| 233 match.contents = shortcut.match_core.contents; |
| 234 match.contents_class = AutocompleteMatch::ClassificationsFromString( |
| 235 shortcut.match_core.contents_class); |
| 236 match.description = shortcut.match_core.description; |
| 237 match.description_class = AutocompleteMatch::ClassificationsFromString( |
| 238 shortcut.match_core.description_class); |
| 239 match.transition = |
| 240 static_cast<content::PageTransition>(shortcut.match_core.transition); |
| 241 match.type = static_cast<AutocompleteMatch::Type>(shortcut.match_core.type); |
| 242 match.keyword = shortcut.match_core.keyword; |
231 match.RecordAdditionalInfo("number of hits", shortcut.number_of_hits); | 243 match.RecordAdditionalInfo("number of hits", shortcut.number_of_hits); |
232 match.RecordAdditionalInfo("last access time", shortcut.last_access_time); | 244 match.RecordAdditionalInfo("last access time", shortcut.last_access_time); |
233 match.RecordAdditionalInfo("original input text", | 245 match.RecordAdditionalInfo("original input text", |
234 base::UTF16ToUTF8(shortcut.text)); | 246 base::UTF16ToUTF8(shortcut.text)); |
235 | 247 |
236 // Set |inline_autocompletion| and |allowed_to_be_default_match| if possible. | 248 // Set |inline_autocompletion| and |allowed_to_be_default_match| if possible. |
237 // If the match is a search query this is easy: simply check whether the | 249 // If the match is a search query this is easy: simply check whether the |
238 // user text is a prefix of the query. If the match is a navigation, we | 250 // user text is a prefix of the query. If the match is a navigation, we |
239 // assume the fill_into_edit looks something like a URL, so we use | 251 // assume the fill_into_edit looks something like a URL, so we use |
240 // BestURLPrefix() to try and strip off any prefixes that the user might | 252 // BestURLPrefix() to try and strip off any prefixes that the user might |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 last_position = word_end; | 394 last_position = word_end; |
383 break; | 395 break; |
384 } | 396 } |
385 } | 397 } |
386 last_position = std::max(last_position, next_character); | 398 last_position = std::max(last_position, next_character); |
387 } | 399 } |
388 | 400 |
389 return AutocompleteMatch::MergeClassifications(original_class, match_class); | 401 return AutocompleteMatch::MergeClassifications(original_class, match_class); |
390 } | 402 } |
391 | 403 |
392 history::ShortcutsBackend::ShortcutMap::const_iterator | 404 ShortcutsBackend::ShortcutMap::const_iterator |
393 ShortcutsProvider::FindFirstMatch(const base::string16& keyword, | 405 ShortcutsProvider::FindFirstMatch(const base::string16& keyword, |
394 history::ShortcutsBackend* backend) { | 406 ShortcutsBackend* backend) { |
395 DCHECK(backend); | 407 DCHECK(backend); |
396 history::ShortcutsBackend::ShortcutMap::const_iterator it = | 408 ShortcutsBackend::ShortcutMap::const_iterator it = |
397 backend->shortcuts_map().lower_bound(keyword); | 409 backend->shortcuts_map().lower_bound(keyword); |
398 // Lower bound not necessarily matches the keyword, check for item pointed by | 410 // Lower bound not necessarily matches the keyword, check for item pointed by |
399 // the lower bound iterator to at least start with keyword. | 411 // the lower bound iterator to at least start with keyword. |
400 return ((it == backend->shortcuts_map().end()) || | 412 return ((it == backend->shortcuts_map().end()) || |
401 StartsWith(it->first, keyword, true)) ? it : | 413 StartsWith(it->first, keyword, true)) ? it : |
402 backend->shortcuts_map().end(); | 414 backend->shortcuts_map().end(); |
403 } | 415 } |
404 | 416 |
405 int ShortcutsProvider::CalculateScore( | 417 int ShortcutsProvider::CalculateScore( |
406 const base::string16& terms, | 418 const base::string16& terms, |
407 const history::ShortcutsBackend::Shortcut& shortcut, | 419 const history::ShortcutsDatabase::Shortcut& shortcut, |
408 int max_relevance) { | 420 int max_relevance) { |
409 DCHECK(!terms.empty()); | 421 DCHECK(!terms.empty()); |
410 DCHECK_LE(terms.length(), shortcut.text.length()); | 422 DCHECK_LE(terms.length(), shortcut.text.length()); |
411 | 423 |
412 // The initial score is based on how much of the shortcut the user has typed. | 424 // The initial score is based on how much of the shortcut the user has typed. |
413 // Using the square root of the typed fraction boosts the base score rapidly | 425 // Using the square root of the typed fraction boosts the base score rapidly |
414 // as characters are typed, compared with simply using the typed fraction | 426 // as characters are typed, compared with simply using the typed fraction |
415 // directly. This makes sense since the first characters typed are much more | 427 // directly. This makes sense since the first characters typed are much more |
416 // important for determining how likely it is a user wants a particular | 428 // important for determining how likely it is a user wants a particular |
417 // shortcut than are the remaining continued characters. | 429 // shortcut than are the remaining continued characters. |
(...skipping 13 matching lines...) Expand all Loading... |
431 // (1.0 / each 5 additional hits), up to a maximum of 5x as long. | 443 // (1.0 / each 5 additional hits), up to a maximum of 5x as long. |
432 const double kMaxDecaySpeedDivisor = 5.0; | 444 const double kMaxDecaySpeedDivisor = 5.0; |
433 const double kNumUsesPerDecaySpeedDivisorIncrement = 5.0; | 445 const double kNumUsesPerDecaySpeedDivisorIncrement = 5.0; |
434 double decay_divisor = std::min(kMaxDecaySpeedDivisor, | 446 double decay_divisor = std::min(kMaxDecaySpeedDivisor, |
435 (shortcut.number_of_hits + kNumUsesPerDecaySpeedDivisorIncrement - 1) / | 447 (shortcut.number_of_hits + kNumUsesPerDecaySpeedDivisorIncrement - 1) / |
436 kNumUsesPerDecaySpeedDivisorIncrement); | 448 kNumUsesPerDecaySpeedDivisorIncrement); |
437 | 449 |
438 return static_cast<int>((base_score / exp(decay_exponent / decay_divisor)) + | 450 return static_cast<int>((base_score / exp(decay_exponent / decay_divisor)) + |
439 0.5); | 451 0.5); |
440 } | 452 } |
OLD | NEW |