Chromium Code Reviews| 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); |
|
Mark P
2014/04/09 22:03:17
I realized we don't need to check the return value
| |
| 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: | |
| 273 // URLs are fixed up to the chrome:// scheme.) | |
| 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( | 249 match.inline_autocompletion = match.fill_into_edit.substr( |
|
Peter Kasting
2014/04/10 00:22:34
Nit: Wrap after '=' instead
Mark P
2014/04/10 17:10:02
Done.
| |
| 282 best_prefix->prefix.length() + matching_string->length()); | 250 inline_autocomplete_offset); |
| 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 |