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 "components/omnibox/browser/bookmark_provider.h" | 5 #include "components/omnibox/browser/bookmark_provider.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <functional> | 8 #include <functional> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/macros.h" | 11 #include "base/macros.h" |
| 12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
| 13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
| 14 #include "base/trace_event/trace_event.h" | 14 #include "base/trace_event/trace_event.h" |
| 15 #include "components/bookmarks/browser/bookmark_model.h" | 15 #include "components/bookmarks/browser/bookmark_model.h" |
| 16 #include "components/bookmarks/browser/titled_url_match.h" | 16 #include "components/bookmarks/browser/titled_url_match.h" |
| 17 #include "components/metrics/proto/omnibox_input_type.pb.h" | 17 #include "components/metrics/proto/omnibox_input_type.pb.h" |
| 18 #include "components/omnibox/browser/autocomplete_provider_client.h" | 18 #include "components/omnibox/browser/autocomplete_provider_client.h" |
| 19 #include "components/omnibox/browser/autocomplete_provider_utils.h" | |
| 19 #include "components/omnibox/browser/autocomplete_result.h" | 20 #include "components/omnibox/browser/autocomplete_result.h" |
| 20 #include "components/omnibox/browser/history_provider.h" | |
| 21 #include "components/omnibox/browser/url_prefix.h" | |
| 22 #include "components/prefs/pref_service.h" | 21 #include "components/prefs/pref_service.h" |
| 23 #include "components/url_formatter/url_formatter.h" | |
| 24 #include "url/url_constants.h" | 22 #include "url/url_constants.h" |
| 25 | 23 |
| 26 using bookmarks::BookmarkNode; | 24 using bookmarks::BookmarkNode; |
| 27 using bookmarks::TitledUrlMatch; | 25 using bookmarks::TitledUrlMatch; |
| 28 using TitledUrlMatches = std::vector<TitledUrlMatch>; | 26 using TitledUrlMatches = std::vector<TitledUrlMatch>; |
| 29 | 27 |
| 30 namespace { | |
| 31 | |
| 32 // Removes leading spaces from |title| before displaying, otherwise it looks | |
| 33 // funny. In the process, corrects |title_match_positions| so the correct | |
| 34 // characters are highlighted. | |
| 35 void CorrectTitleAndMatchPositions( | |
| 36 base::string16* title, | |
| 37 TitledUrlMatch::MatchPositions* title_match_positions) { | |
| 38 size_t leading_whitespace_chars = title->length(); | |
| 39 base::TrimWhitespace(*title, base::TRIM_LEADING, title); | |
| 40 leading_whitespace_chars-= title->length(); | |
| 41 if (leading_whitespace_chars == 0) | |
| 42 return; | |
| 43 for (query_parser::Snippet::MatchPositions::iterator it = | |
| 44 title_match_positions->begin(); | |
| 45 it != title_match_positions->end(); ++it) { | |
| 46 (*it) = query_parser::Snippet::MatchPosition( | |
| 47 it->first - leading_whitespace_chars, | |
| 48 it->second - leading_whitespace_chars); | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 } // namespace | |
| 53 | |
| 54 // BookmarkProvider ------------------------------------------------------------ | 28 // BookmarkProvider ------------------------------------------------------------ |
| 55 | 29 |
| 56 BookmarkProvider::BookmarkProvider(AutocompleteProviderClient* client) | 30 BookmarkProvider::BookmarkProvider(AutocompleteProviderClient* client) |
| 57 : AutocompleteProvider(AutocompleteProvider::TYPE_BOOKMARK), | 31 : AutocompleteProvider(AutocompleteProvider::TYPE_BOOKMARK), |
| 58 client_(client), | 32 client_(client), |
| 59 bookmark_model_(client ? client_->GetBookmarkModel() : nullptr) {} | 33 bookmark_model_(client ? client_->GetBookmarkModel() : nullptr) {} |
| 60 | 34 |
| 61 void BookmarkProvider::Start(const AutocompleteInput& input, | 35 void BookmarkProvider::Start(const AutocompleteInput& input, |
| 62 bool minimal_changes) { | 36 bool minimal_changes) { |
| 63 TRACE_EVENT0("omnibox", "BookmarkProvider::Start"); | 37 TRACE_EVENT0("omnibox", "BookmarkProvider::Start"); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 100 // | 74 // |
| 101 // Please refer to the code for TitledUrlIndex::GetResultsMatching for | 75 // Please refer to the code for TitledUrlIndex::GetResultsMatching for |
| 102 // complete details of how searches are performed against the user's | 76 // complete details of how searches are performed against the user's |
| 103 // bookmarks. | 77 // bookmarks. |
| 104 bookmark_model_->GetBookmarksMatching(input.text(), | 78 bookmark_model_->GetBookmarksMatching(input.text(), |
| 105 kMaxBookmarkMatches, | 79 kMaxBookmarkMatches, |
| 106 &matches); | 80 &matches); |
| 107 if (matches.empty()) | 81 if (matches.empty()) |
| 108 return; // There were no matches. | 82 return; // There were no matches. |
| 109 const base::string16 fixed_up_input(FixupUserInput(input).second); | 83 const base::string16 fixed_up_input(FixupUserInput(input).second); |
| 110 for (TitledUrlMatches::const_iterator i = matches.begin(); i != matches.end(); | 84 for (auto bookmark_match : matches) { |
|
Mark P
2016/12/23 22:38:50
Can this auto be const and/or &?
mattreynolds
2017/01/04 00:58:03
Done.
| |
| 111 ++i) { | 85 // Score the TitledUrlMatch. If its score is greater than 0 then the |
| 112 // Create and score the AutocompleteMatch. If its score is 0 then the | 86 // AutocompleteMatch is created and added to matches_. |
| 113 // match is discarded. | 87 int relevance = CalculateBookmarkMatchRelevance(bookmark_match); |
| 114 AutocompleteMatch match(TitledUrlMatchToACMatch(input, fixed_up_input, *i)); | 88 if (relevance > 0) { |
| 115 if (match.relevance > 0) | 89 matches_.push_back(TitledUrlMatchToAutocompleteMatch( |
| 116 matches_.push_back(match); | 90 this, client_->GetSchemeClassifier(), input, fixed_up_input, |
| 91 bookmark_match, AutocompleteMatchType::BOOKMARK_TITLE, relevance)); | |
| 92 } | |
| 117 } | 93 } |
| 118 | 94 |
| 119 // Sort and clip the resulting matches. | 95 // Sort and clip the resulting matches. |
| 120 size_t num_matches = | 96 size_t num_matches = |
| 121 std::min(matches_.size(), AutocompleteProvider::kMaxMatches); | 97 std::min(matches_.size(), AutocompleteProvider::kMaxMatches); |
| 122 std::partial_sort(matches_.begin(), matches_.begin() + num_matches, | 98 std::partial_sort(matches_.begin(), matches_.begin() + num_matches, |
| 123 matches_.end(), AutocompleteMatch::MoreRelevant); | 99 matches_.end(), AutocompleteMatch::MoreRelevant); |
| 124 matches_.resize(num_matches); | 100 matches_.resize(num_matches); |
| 125 } | 101 } |
| 126 | 102 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 148 | 124 |
| 149 double ScoringFactor() { return scoring_factor_; } | 125 double ScoringFactor() { return scoring_factor_; } |
| 150 | 126 |
| 151 private: | 127 private: |
| 152 double title_length_; | 128 double title_length_; |
| 153 double scoring_factor_; | 129 double scoring_factor_; |
| 154 }; | 130 }; |
| 155 | 131 |
| 156 } // namespace | 132 } // namespace |
| 157 | 133 |
| 158 AutocompleteMatch BookmarkProvider::TitledUrlMatchToACMatch( | 134 int BookmarkProvider::CalculateBookmarkMatchRelevance( |
| 159 const AutocompleteInput& input, | |
| 160 const base::string16& fixed_up_input_text, | |
| 161 const TitledUrlMatch& bookmark_match) { | 135 const TitledUrlMatch& bookmark_match) { |
| 162 // The AutocompleteMatch we construct is non-deletable because the only | |
| 163 // way to support this would be to delete the underlying bookmark, which is | |
| 164 // unlikely to be what the user intends. | |
| 165 AutocompleteMatch match(this, 0, false, | |
| 166 AutocompleteMatchType::BOOKMARK_TITLE); | |
| 167 base::string16 title(bookmark_match.node->GetTitledUrlNodeTitle()); | |
| 168 TitledUrlMatch::MatchPositions new_title_match_positions = | |
| 169 bookmark_match.title_match_positions; | |
| 170 CorrectTitleAndMatchPositions(&title, &new_title_match_positions); | |
| 171 const GURL& url(bookmark_match.node->GetTitledUrlNodeUrl()); | |
| 172 const base::string16& url_utf16 = base::UTF8ToUTF16(url.spec()); | |
| 173 size_t inline_autocomplete_offset = URLPrefix::GetInlineAutocompleteOffset( | |
| 174 input.text(), fixed_up_input_text, false, url_utf16); | |
| 175 match.destination_url = url; | |
| 176 const size_t match_start = bookmark_match.url_match_positions.empty() ? | |
| 177 0 : bookmark_match.url_match_positions[0].first; | |
| 178 const bool trim_http = !AutocompleteInput::HasHTTPScheme(input.text()) && | |
| 179 ((match_start == base::string16::npos) || (match_start != 0)); | |
| 180 std::vector<size_t> offsets = TitledUrlMatch::OffsetsFromMatchPositions( | |
| 181 bookmark_match.url_match_positions); | |
| 182 // In addition to knowing how |offsets| is transformed, we need to know how | |
| 183 // |inline_autocomplete_offset| is transformed. We add it to the end of | |
| 184 // |offsets|, compute how everything is transformed, then remove it from the | |
| 185 // end. | |
| 186 offsets.push_back(inline_autocomplete_offset); | |
| 187 match.contents = url_formatter::FormatUrlWithOffsets( | |
| 188 url, url_formatter::kFormatUrlOmitAll & | |
| 189 ~(trim_http ? 0 : url_formatter::kFormatUrlOmitHTTP), | |
| 190 net::UnescapeRule::SPACES, nullptr, nullptr, &offsets); | |
| 191 inline_autocomplete_offset = offsets.back(); | |
| 192 offsets.pop_back(); | |
| 193 TitledUrlMatch::MatchPositions new_url_match_positions = | |
| 194 TitledUrlMatch::ReplaceOffsetsInMatchPositions( | |
| 195 bookmark_match.url_match_positions, offsets); | |
| 196 match.contents_class = | |
| 197 ClassificationsFromMatch(new_url_match_positions, | |
| 198 match.contents.size(), | |
| 199 true); | |
| 200 match.fill_into_edit = | |
| 201 AutocompleteInput::FormattedStringWithEquivalentMeaning( | |
| 202 url, match.contents, client_->GetSchemeClassifier()); | |
| 203 if (inline_autocomplete_offset != base::string16::npos) { | |
| 204 // |inline_autocomplete_offset| may be beyond the end of the | |
| 205 // |fill_into_edit| if the user has typed an URL with a scheme and the | |
| 206 // last character typed is a slash. That slash is removed by the | |
| 207 // FormatURLWithOffsets call above. | |
| 208 if (inline_autocomplete_offset < match.fill_into_edit.length()) { | |
| 209 match.inline_autocompletion = | |
| 210 match.fill_into_edit.substr(inline_autocomplete_offset); | |
| 211 } | |
| 212 match.allowed_to_be_default_match = match.inline_autocompletion.empty() || | |
| 213 !HistoryProvider::PreventInlineAutocomplete(input); | |
| 214 } | |
| 215 match.description = title; | |
| 216 match.description_class = | |
| 217 ClassificationsFromMatch(bookmark_match.title_match_positions, | |
| 218 match.description.size(), | |
| 219 false); | |
| 220 | |
| 221 // Summary on how a relevance score is determined for the match: | 136 // Summary on how a relevance score is determined for the match: |
| 222 // | 137 // |
| 223 // For each match within the bookmark's title or URL (or both), calculate a | 138 // For each match within the bookmark's title or URL (or both), calculate a |
| 224 // 'factor', sum up those factors, then use the sum to figure out a value | 139 // 'factor', sum up those factors, then use the sum to figure out a value |
| 225 // between the base score and the maximum score. | 140 // between the base score and the maximum score. |
| 226 // | 141 // |
| 227 // The factor for each match is the product of: | 142 // The factor for each match is the product of: |
| 228 // | 143 // |
| 229 // 1) how many characters in the bookmark's title/URL are part of this match. | 144 // 1) how many characters in the bookmark's title/URL are part of this match. |
| 230 // This is capped at the length of the bookmark's title | 145 // This is capped at the length of the bookmark's title |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 261 // possible score. This product is added to the minimum possible score to | 176 // possible score. This product is added to the minimum possible score to |
| 262 // give the preliminary score. | 177 // give the preliminary score. |
| 263 // | 178 // |
| 264 // If the preliminary score is less than the maximum possible score, 1199, | 179 // If the preliminary score is less than the maximum possible score, 1199, |
| 265 // it can be boosted up to that maximum possible score if the URL referenced | 180 // it can be boosted up to that maximum possible score if the URL referenced |
| 266 // by the bookmark is also referenced by any of the user's other bookmarks. | 181 // by the bookmark is also referenced by any of the user's other bookmarks. |
| 267 // A count of how many times the bookmark's URL is referenced is determined | 182 // A count of how many times the bookmark's URL is referenced is determined |
| 268 // and, for each additional reference beyond the one for the bookmark being | 183 // and, for each additional reference beyond the one for the bookmark being |
| 269 // scored up to a maximum of three, the score is boosted by a fixed amount | 184 // scored up to a maximum of three, the score is boosted by a fixed amount |
| 270 // given by |kURLCountBoost|, below. | 185 // given by |kURLCountBoost|, below. |
| 271 // | 186 |
| 187 base::string16 title(bookmark_match.node->GetTitledUrlNodeTitle()); | |
| 188 const GURL& url(bookmark_match.node->GetTitledUrlNodeUrl()); | |
| 272 | 189 |
| 273 // Pretend empty titles are identical to the URL. | 190 // Pretend empty titles are identical to the URL. |
| 274 if (title.empty()) | 191 if (title.empty()) |
| 275 title = base::ASCIIToUTF16(url.spec()); | 192 title = base::ASCIIToUTF16(url.spec()); |
| 276 ScoringFunctor title_position_functor = | 193 ScoringFunctor title_position_functor = |
| 277 for_each(bookmark_match.title_match_positions.begin(), | 194 for_each(bookmark_match.title_match_positions.begin(), |
| 278 bookmark_match.title_match_positions.end(), | 195 bookmark_match.title_match_positions.end(), |
| 279 ScoringFunctor(title.size())); | 196 ScoringFunctor(title.size())); |
| 280 ScoringFunctor url_position_functor = | 197 ScoringFunctor url_position_functor = |
| 281 for_each(bookmark_match.url_match_positions.begin(), | 198 for_each(bookmark_match.url_match_positions.begin(), |
| 282 bookmark_match.url_match_positions.end(), | 199 bookmark_match.url_match_positions.end(), |
| 283 ScoringFunctor( | 200 ScoringFunctor( |
| 284 bookmark_match.node->GetTitledUrlNodeUrl().spec().length())); | 201 bookmark_match.node->GetTitledUrlNodeUrl().spec().length())); |
| 285 const double title_match_strength = title_position_functor.ScoringFactor(); | 202 const double title_match_strength = title_position_functor.ScoringFactor(); |
| 286 const double summed_factors = title_match_strength + | 203 const double summed_factors = title_match_strength + |
| 287 url_position_functor.ScoringFactor(); | 204 url_position_functor.ScoringFactor(); |
| 288 const double normalized_sum = | 205 const double normalized_sum = |
| 289 std::min(summed_factors / (title.size() + 10), 1.0); | 206 std::min(summed_factors / (title.size() + 10), 1.0); |
| 290 // Bookmarks with javascript scheme ("bookmarklets") that do not have title | 207 // Bookmarks with javascript scheme ("bookmarklets") that do not have title |
| 291 // matches get a lower base and lower maximum score because returning them | 208 // matches get a lower base and lower maximum score because returning them |
| 292 // for matches in their (often very long) URL looks stupid and is often not | 209 // for matches in their (often very long) URL looks stupid and is often not |
| 293 // intended by the user. | 210 // intended by the user. |
| 294 const bool bookmarklet_without_title_match = | 211 const bool bookmarklet_without_title_match = |
| 295 url.SchemeIs(url::kJavaScriptScheme) && (title_match_strength == 0.0); | 212 url.SchemeIs(url::kJavaScriptScheme) && (title_match_strength == 0.0); |
| 296 const int kBaseBookmarkScore = bookmarklet_without_title_match ? 400 : 900; | 213 const int kBaseBookmarkScore = bookmarklet_without_title_match ? 400 : 900; |
| 297 const int kMaxBookmarkScore = bookmarklet_without_title_match ? 799 : 1199; | 214 const int kMaxBookmarkScore = bookmarklet_without_title_match ? 799 : 1199; |
| 298 const double kBookmarkScoreRange = | 215 const double kBookmarkScoreRange = |
| 299 static_cast<double>(kMaxBookmarkScore - kBaseBookmarkScore); | 216 static_cast<double>(kMaxBookmarkScore - kBaseBookmarkScore); |
| 300 match.relevance = static_cast<int>(normalized_sum * kBookmarkScoreRange) + | 217 int relevance = static_cast<int>(normalized_sum * kBookmarkScoreRange) + |
| 301 kBaseBookmarkScore; | 218 kBaseBookmarkScore; |
| 302 // Don't waste any time searching for additional referenced URLs if we | 219 // Don't waste any time searching for additional referenced URLs if we |
| 303 // already have a perfect title match. | 220 // already have a perfect title match. |
| 304 if (match.relevance >= kMaxBookmarkScore) | 221 if (relevance >= kMaxBookmarkScore) |
| 305 return match; | 222 return relevance; |
| 306 // Boost the score if the bookmark's URL is referenced by other bookmarks. | 223 // Boost the score if the bookmark's URL is referenced by other bookmarks. |
| 307 const int kURLCountBoost[4] = { 0, 75, 125, 150 }; | 224 const int kURLCountBoost[4] = { 0, 75, 125, 150 }; |
| 308 std::vector<const BookmarkNode*> nodes; | 225 std::vector<const BookmarkNode*> nodes; |
| 309 bookmark_model_->GetNodesByURL(url, &nodes); | 226 bookmark_model_->GetNodesByURL(url, &nodes); |
| 310 DCHECK_GE(std::min(arraysize(kURLCountBoost), nodes.size()), 1U); | 227 DCHECK_GE(std::min(arraysize(kURLCountBoost), nodes.size()), 1U); |
| 311 match.relevance += | 228 relevance += |
| 312 kURLCountBoost[std::min(arraysize(kURLCountBoost), nodes.size()) - 1]; | 229 kURLCountBoost[std::min(arraysize(kURLCountBoost), nodes.size()) - 1]; |
| 313 match.relevance = std::min(kMaxBookmarkScore, match.relevance); | 230 relevance = std::min(kMaxBookmarkScore, relevance); |
| 314 return match; | 231 return relevance; |
| 315 } | 232 } |
| 316 | |
| 317 // static | |
| 318 ACMatchClassifications BookmarkProvider::ClassificationsFromMatch( | |
| 319 const query_parser::Snippet::MatchPositions& positions, | |
| 320 size_t text_length, | |
| 321 bool is_url) { | |
| 322 ACMatchClassification::Style url_style = | |
| 323 is_url ? ACMatchClassification::URL : ACMatchClassification::NONE; | |
| 324 ACMatchClassifications classifications; | |
| 325 if (positions.empty()) { | |
| 326 if (text_length > 0) | |
| 327 classifications.push_back(ACMatchClassification(0, url_style)); | |
| 328 return classifications; | |
| 329 } | |
| 330 | |
| 331 for (query_parser::Snippet::MatchPositions::const_iterator i = | |
| 332 positions.begin(); | |
| 333 i != positions.end(); | |
| 334 ++i) { | |
| 335 AutocompleteMatch::ACMatchClassifications new_class; | |
| 336 AutocompleteMatch::ClassifyLocationInString(i->first, i->second - i->first, | |
| 337 text_length, url_style, &new_class); | |
| 338 classifications = AutocompleteMatch::MergeClassifications( | |
| 339 classifications, new_class); | |
| 340 } | |
| 341 return classifications; | |
| 342 } | |
| OLD | NEW |