| 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 |