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> |
(...skipping 29 matching lines...) Expand all Loading... |
40 class DestinationURLEqualsURL { | 40 class DestinationURLEqualsURL { |
41 public: | 41 public: |
42 explicit DestinationURLEqualsURL(const GURL& url) : url_(url) {} | 42 explicit DestinationURLEqualsURL(const GURL& url) : url_(url) {} |
43 bool operator()(const AutocompleteMatch& match) const { | 43 bool operator()(const AutocompleteMatch& match) const { |
44 return match.destination_url == url_; | 44 return match.destination_url == url_; |
45 } | 45 } |
46 private: | 46 private: |
47 const GURL url_; | 47 const GURL url_; |
48 }; | 48 }; |
49 | 49 |
50 // Like URLPrefix::BestURLPrefix() except also handles the prefix of | |
51 // "www.". This is needed because sometimes the string we're matching | |
52 // against here (which comes from |fill_into_edit|) can start with | |
53 // "www." without having a protocol at the beginning. Because "www." | |
54 // is not on the default prefix list, we test for it explicitly here | |
55 // and use that match if the default list didn't have a match or the | |
56 // default list's match was shorter than it could've been. | |
57 const URLPrefix* BestURLPrefixWithWWWCase( | |
58 const base::string16& text, | |
59 const base::string16& prefix_suffix) { | |
60 CR_DEFINE_STATIC_LOCAL(URLPrefix, www_prefix, | |
61 (base::ASCIIToUTF16("www."), 1)); | |
62 const URLPrefix* best_prefix = URLPrefix::BestURLPrefix(text, prefix_suffix); | |
63 if ((best_prefix == NULL) || | |
64 (best_prefix->num_components < www_prefix.num_components)) { | |
65 if (URLPrefix::PrefixMatch(www_prefix, text, prefix_suffix)) | |
66 best_prefix = &www_prefix; | |
67 } | |
68 return best_prefix; | |
69 } | |
70 | |
71 } // namespace | 50 } // namespace |
72 | 51 |
73 ShortcutsProvider::ShortcutsProvider(AutocompleteProviderListener* listener, | 52 ShortcutsProvider::ShortcutsProvider(AutocompleteProviderListener* listener, |
74 Profile* profile) | 53 Profile* profile) |
75 : AutocompleteProvider(listener, profile, | 54 : AutocompleteProvider(listener, profile, |
76 AutocompleteProvider::TYPE_SHORTCUTS), | 55 AutocompleteProvider::TYPE_SHORTCUTS), |
77 languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)), | 56 languages_(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)), |
78 initialized_(false) { | 57 initialized_(false) { |
79 scoped_refptr<ShortcutsBackend> backend = | 58 scoped_refptr<ShortcutsBackend> backend = |
80 ShortcutsBackendFactory::GetForProfile(profile_); | 59 ShortcutsBackendFactory::GetForProfile(profile_); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 void ShortcutsProvider::GetMatches(const AutocompleteInput& input) { | 130 void ShortcutsProvider::GetMatches(const AutocompleteInput& input) { |
152 scoped_refptr<ShortcutsBackend> backend = | 131 scoped_refptr<ShortcutsBackend> backend = |
153 ShortcutsBackendFactory::GetForProfileIfExists(profile_); | 132 ShortcutsBackendFactory::GetForProfileIfExists(profile_); |
154 if (!backend.get()) | 133 if (!backend.get()) |
155 return; | 134 return; |
156 // Get the URLs from the shortcuts database with keys that partially or | 135 // Get the URLs from the shortcuts database with keys that partially or |
157 // completely match the search term. | 136 // completely match the search term. |
158 base::string16 term_string(base::i18n::ToLower(input.text())); | 137 base::string16 term_string(base::i18n::ToLower(input.text())); |
159 DCHECK(!term_string.empty()); | 138 DCHECK(!term_string.empty()); |
160 | 139 |
161 base::string16 fixed_up_term_string(term_string); | |
162 AutocompleteInput fixed_up_input(input); | 140 AutocompleteInput fixed_up_input(input); |
163 if (FixupUserInput(&fixed_up_input)) | 141 FixupUserInput(&fixed_up_input); |
164 fixed_up_term_string = fixed_up_input.text(); | 142 const GURL& input_as_gurl = URLFixerUpper::FixupURL( |
165 const GURL& term_string_as_gurl = URLFixerUpper::FixupURL( | 143 base::UTF16ToUTF8(input.text()), std::string()); |
166 base::UTF16ToUTF8(term_string), std::string()); | |
167 | 144 |
168 int max_relevance; | 145 int max_relevance; |
169 if (!OmniboxFieldTrial::ShortcutsScoringMaxRelevance( | 146 if (!OmniboxFieldTrial::ShortcutsScoringMaxRelevance( |
170 input.current_page_classification(), &max_relevance)) | 147 input.current_page_classification(), &max_relevance)) |
171 max_relevance = AutocompleteResult::kLowestDefaultScore - 1; | 148 max_relevance = AutocompleteResult::kLowestDefaultScore - 1; |
172 | 149 |
173 for (ShortcutsBackend::ShortcutMap::const_iterator it = | 150 for (ShortcutsBackend::ShortcutMap::const_iterator it = |
174 FindFirstMatch(term_string, backend.get()); | 151 FindFirstMatch(term_string, backend.get()); |
175 it != backend->shortcuts_map().end() && | 152 it != backend->shortcuts_map().end() && |
176 StartsWith(it->first, term_string, true); ++it) { | 153 StartsWith(it->first, term_string, true); ++it) { |
177 // Don't return shortcuts with zero relevance. | 154 // Don't return shortcuts with zero relevance. |
178 int relevance = CalculateScore(term_string, it->second, max_relevance); | 155 int relevance = CalculateScore(term_string, it->second, max_relevance); |
179 if (relevance) { | 156 if (relevance) { |
180 matches_.push_back(ShortcutToACMatch( | 157 matches_.push_back(ShortcutToACMatch( |
181 it->second, relevance, term_string, fixed_up_term_string, | 158 it->second, relevance, input, fixed_up_input, input_as_gurl)); |
182 term_string_as_gurl, input.prevent_inline_autocomplete())); | |
183 matches_.back().ComputeStrippedDestinationURL(profile_); | 159 matches_.back().ComputeStrippedDestinationURL(profile_); |
184 } | 160 } |
185 } | 161 } |
186 // Remove duplicates. | 162 // Remove duplicates. |
187 std::sort(matches_.begin(), matches_.end(), | 163 std::sort(matches_.begin(), matches_.end(), |
188 &AutocompleteMatch::DestinationSortFunc); | 164 &AutocompleteMatch::DestinationSortFunc); |
189 matches_.erase(std::unique(matches_.begin(), matches_.end(), | 165 matches_.erase(std::unique(matches_.begin(), matches_.end(), |
190 &AutocompleteMatch::DestinationsEqual), | 166 &AutocompleteMatch::DestinationsEqual), |
191 matches_.end()); | 167 matches_.end()); |
192 // Find best matches. | 168 // Find best matches. |
(...skipping 21 matching lines...) Expand all Loading... |
214 max_relevance = std::min(max_relevance, it->relevance); | 190 max_relevance = std::min(max_relevance, it->relevance); |
215 it->relevance = max_relevance; | 191 it->relevance = max_relevance; |
216 if (max_relevance > 1) | 192 if (max_relevance > 1) |
217 --max_relevance; | 193 --max_relevance; |
218 } | 194 } |
219 } | 195 } |
220 | 196 |
221 AutocompleteMatch ShortcutsProvider::ShortcutToACMatch( | 197 AutocompleteMatch ShortcutsProvider::ShortcutToACMatch( |
222 const history::ShortcutsDatabase::Shortcut& shortcut, | 198 const history::ShortcutsDatabase::Shortcut& shortcut, |
223 int relevance, | 199 int relevance, |
224 const base::string16& term_string, | 200 const AutocompleteInput& input, |
225 const base::string16& fixed_up_term_string, | 201 const AutocompleteInput& fixed_up_input, |
226 const GURL& term_string_as_gurl, | 202 const GURL& input_as_gurl) { |
227 const bool prevent_inline_autocomplete) { | 203 DCHECK(!input.text().empty()); |
228 DCHECK(!term_string.empty()); | |
229 AutocompleteMatch match; | 204 AutocompleteMatch match; |
230 match.provider = this; | 205 match.provider = this; |
231 match.relevance = relevance; | 206 match.relevance = relevance; |
232 match.deletable = true; | 207 match.deletable = true; |
233 match.fill_into_edit = shortcut.match_core.fill_into_edit; | 208 match.fill_into_edit = shortcut.match_core.fill_into_edit; |
234 match.destination_url = shortcut.match_core.destination_url; | 209 match.destination_url = shortcut.match_core.destination_url; |
235 DCHECK(match.destination_url.is_valid()); | 210 DCHECK(match.destination_url.is_valid()); |
236 match.contents = shortcut.match_core.contents; | 211 match.contents = shortcut.match_core.contents; |
237 match.contents_class = AutocompleteMatch::ClassificationsFromString( | 212 match.contents_class = AutocompleteMatch::ClassificationsFromString( |
238 shortcut.match_core.contents_class); | 213 shortcut.match_core.contents_class); |
239 match.description = shortcut.match_core.description; | 214 match.description = shortcut.match_core.description; |
240 match.description_class = AutocompleteMatch::ClassificationsFromString( | 215 match.description_class = AutocompleteMatch::ClassificationsFromString( |
241 shortcut.match_core.description_class); | 216 shortcut.match_core.description_class); |
242 match.transition = | 217 match.transition = |
243 static_cast<content::PageTransition>(shortcut.match_core.transition); | 218 static_cast<content::PageTransition>(shortcut.match_core.transition); |
244 match.type = static_cast<AutocompleteMatch::Type>(shortcut.match_core.type); | 219 match.type = static_cast<AutocompleteMatch::Type>(shortcut.match_core.type); |
245 match.keyword = shortcut.match_core.keyword; | 220 match.keyword = shortcut.match_core.keyword; |
246 match.RecordAdditionalInfo("number of hits", shortcut.number_of_hits); | 221 match.RecordAdditionalInfo("number of hits", shortcut.number_of_hits); |
247 match.RecordAdditionalInfo("last access time", shortcut.last_access_time); | 222 match.RecordAdditionalInfo("last access time", shortcut.last_access_time); |
248 match.RecordAdditionalInfo("original input text", | 223 match.RecordAdditionalInfo("original input text", |
249 base::UTF16ToUTF8(shortcut.text)); | 224 base::UTF16ToUTF8(shortcut.text)); |
250 | 225 |
251 // Set |inline_autocompletion| and |allowed_to_be_default_match| if possible. | 226 // Set |inline_autocompletion| and |allowed_to_be_default_match| if possible. |
252 // If the match is a search query this is easy: simply check whether the | 227 // If the match is a search query this is easy: simply check whether the |
253 // user text is a prefix of the query. If the match is a navigation, we | 228 // user text is a prefix of the query. If the match is a navigation, we |
254 // assume the fill_into_edit looks something like a URL, so we use | 229 // assume the fill_into_edit looks something like a URL, so we use |
255 // BestURLPrefix() to try and strip off any prefixes that the user might | 230 // URLPrefix::ComputeMatchStartAndInlineAutocompleteOffset() to try and strip |
256 // not think would change the meaning, but would otherwise prevent inline | 231 // off any prefixes that the user might not think would change the meaning, |
257 // autocompletion. This allows, for example, the input of "foo.c" to | 232 // but would otherwise prevent inline autocompletion. This allows, for |
258 // autocomplete to "foo.com" for a fill_into_edit of "http://foo.com". | 233 // example, the input of "foo.c" to autocomplete to "foo.com" for a |
| 234 // fill_into_edit of "http://foo.com". |
259 if (AutocompleteMatch::IsSearchType(match.type)) { | 235 if (AutocompleteMatch::IsSearchType(match.type)) { |
260 if (StartsWith(match.fill_into_edit, term_string, false)) { | 236 if (StartsWith(match.fill_into_edit, input.text(), false)) { |
261 match.inline_autocompletion = | 237 match.inline_autocompletion = |
262 match.fill_into_edit.substr(term_string.length()); | 238 match.fill_into_edit.substr(input.text().length()); |
263 match.allowed_to_be_default_match = | 239 match.allowed_to_be_default_match = |
264 !prevent_inline_autocomplete || match.inline_autocompletion.empty(); | 240 !input.prevent_inline_autocomplete() || |
| 241 match.inline_autocompletion.empty(); |
265 } | 242 } |
266 } else { | 243 } else { |
267 const URLPrefix* best_prefix = | 244 size_t match_start, inline_autocomplete_offset; |
268 BestURLPrefixWithWWWCase(match.fill_into_edit, term_string); | 245 URLPrefix::ComputeMatchStartAndInlineAutocompleteOffset( |
269 const base::string16* matching_string = &term_string; | 246 input, fixed_up_input, true, match.fill_into_edit, |
270 // If we failed to find a best_prefix initially, try again using a | 247 &match_start, &inline_autocomplete_offset); |
271 // fixed-up version of the user input. This is especially useful to | 248 if (inline_autocomplete_offset != base::string16::npos) { |
272 // get about: URLs to inline against chrome:// shortcuts. (about: | 249 match.inline_autocompletion = |
273 // URLs are fixed up to the chrome:// scheme.) | 250 match.fill_into_edit.substr(inline_autocomplete_offset); |
274 if ((best_prefix == NULL) && !fixed_up_term_string.empty() && | |
275 (fixed_up_term_string != term_string)) { | |
276 best_prefix = BestURLPrefixWithWWWCase( | |
277 match.fill_into_edit, fixed_up_term_string); | |
278 matching_string = &fixed_up_term_string; | |
279 } | |
280 if (best_prefix != NULL) { | |
281 match.inline_autocompletion = match.fill_into_edit.substr( | |
282 best_prefix->prefix.length() + matching_string->length()); | |
283 match.allowed_to_be_default_match = | 251 match.allowed_to_be_default_match = |
284 !prevent_inline_autocomplete || match.inline_autocompletion.empty(); | 252 !HistoryProvider::PreventInlineAutocomplete(input) || |
| 253 match.inline_autocompletion.empty(); |
285 } else { | 254 } else { |
286 // Also allow a user's input to be marked as default if it would be fixed | 255 // Also allow a user's input to be marked as default if it would be fixed |
287 // up to the same thing as the fill_into_edit. This handles cases like | 256 // up to the same thing as the fill_into_edit. This handles cases like |
288 // the user input containing a trailing slash absent in fill_into_edit. | 257 // the user input containing a trailing slash absent in fill_into_edit. |
289 match.allowed_to_be_default_match = (term_string_as_gurl == | 258 match.allowed_to_be_default_match = (input_as_gurl == |
290 URLFixerUpper::FixupURL(base::UTF16ToUTF8(match.fill_into_edit), | 259 URLFixerUpper::FixupURL(base::UTF16ToUTF8(match.fill_into_edit), |
291 std::string())); | 260 std::string())); |
292 } | 261 } |
293 } | 262 } |
294 | 263 |
295 // Try to mark pieces of the contents and description as matches if they | 264 // Try to mark pieces of the contents and description as matches if they |
296 // appear in |term_string|. | 265 // appear in |input.text()|. |
| 266 const base::string16 term_string = base::i18n::ToLower(input.text()); |
297 WordMap terms_map(CreateWordMapForString(term_string)); | 267 WordMap terms_map(CreateWordMapForString(term_string)); |
298 if (!terms_map.empty()) { | 268 if (!terms_map.empty()) { |
299 match.contents_class = ClassifyAllMatchesInString(term_string, terms_map, | 269 match.contents_class = ClassifyAllMatchesInString(term_string, terms_map, |
300 match.contents, match.contents_class); | 270 match.contents, match.contents_class); |
301 match.description_class = ClassifyAllMatchesInString(term_string, terms_map, | 271 match.description_class = ClassifyAllMatchesInString(term_string, terms_map, |
302 match.description, match.description_class); | 272 match.description, match.description_class); |
303 } | 273 } |
304 return match; | 274 return match; |
305 } | 275 } |
306 | 276 |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
453 // (1.0 / each 5 additional hits), up to a maximum of 5x as long. | 423 // (1.0 / each 5 additional hits), up to a maximum of 5x as long. |
454 const double kMaxDecaySpeedDivisor = 5.0; | 424 const double kMaxDecaySpeedDivisor = 5.0; |
455 const double kNumUsesPerDecaySpeedDivisorIncrement = 5.0; | 425 const double kNumUsesPerDecaySpeedDivisorIncrement = 5.0; |
456 double decay_divisor = std::min(kMaxDecaySpeedDivisor, | 426 double decay_divisor = std::min(kMaxDecaySpeedDivisor, |
457 (shortcut.number_of_hits + kNumUsesPerDecaySpeedDivisorIncrement - 1) / | 427 (shortcut.number_of_hits + kNumUsesPerDecaySpeedDivisorIncrement - 1) / |
458 kNumUsesPerDecaySpeedDivisorIncrement); | 428 kNumUsesPerDecaySpeedDivisorIncrement); |
459 | 429 |
460 return static_cast<int>((base_score / exp(decay_exponent / decay_divisor)) + | 430 return static_cast<int>((base_score / exp(decay_exponent / decay_divisor)) + |
461 0.5); | 431 0.5); |
462 } | 432 } |
OLD | NEW |