Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 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/search_provider.h" | 5 #include "chrome/browser/autocomplete/search_provider.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cmath> | 8 #include <cmath> |
| 9 | 9 |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 100 } | 100 } |
| 101 } | 101 } |
| 102 return false; | 102 return false; |
| 103 } | 103 } |
| 104 | 104 |
| 105 // Builds the match contents and classification for the contents, and updates | 105 // Builds the match contents and classification for the contents, and updates |
| 106 // the given |AutocompleteMatch|. | 106 // the given |AutocompleteMatch|. |
| 107 void SetAndClassifyMatchContents(const base::string16& query_string, | 107 void SetAndClassifyMatchContents(const base::string16& query_string, |
| 108 const base::string16& input_text, | 108 const base::string16& input_text, |
| 109 const base::string16& match_contents, | 109 const base::string16& match_contents, |
| 110 const base::string16& annotation, | |
| 111 AutocompleteMatch* match) { | 110 AutocompleteMatch* match) { |
| 112 size_t match_contents_start = 0; | 111 match->contents = match_contents.empty() ? query_string : match_contents; |
| 113 size_t annotation_start = match_contents.size(); | |
| 114 // Append annotation if present. | |
| 115 if (annotation.empty()) { | |
| 116 match->contents = match_contents; | |
| 117 } else { | |
| 118 std::vector<size_t> positions; | |
| 119 match->contents = l10n_util::GetStringFUTF16( | |
| 120 IDS_ANNOTATED_SUGGESTION, match_contents, annotation, &positions); | |
|
Peter Kasting
2013/12/10 03:52:30
If we're removing the only reference to IDS_ANNOTA
Anuj
2013/12/10 23:08:09
Done.
| |
| 121 match_contents_start = positions[0]; | |
| 122 annotation_start = positions[1]; | |
| 123 } | |
| 124 size_t match_contents_end = match_contents_start + match_contents.size(); | |
| 125 | |
| 126 if (!annotation.empty() && (annotation_start < match_contents_start)) | |
| 127 match->contents_class.push_back(ACMatchClassification( | |
| 128 annotation_start, ACMatchClassification::DIM)); | |
| 129 | 112 |
| 130 // We do intra-string highlighting for suggestions - the suggested segment | 113 // We do intra-string highlighting for suggestions - the suggested segment |
| 131 // will be highlighted, e.g. for input_text = "you" the suggestion may be | 114 // will be highlighted, e.g. for input_text = "you" the suggestion may be |
| 132 // "youtube", so we'll bold the "tube" section: you*tube*. | 115 // "youtube", so we'll bold the "tube" section: you*tube*. |
| 133 if (input_text != match_contents) { | 116 if (input_text != match_contents) { |
| 134 size_t input_position = match->contents.substr( | 117 size_t input_position = match->contents.find(input_text); |
| 135 match_contents_start, match_contents.length()).find(input_text); | |
| 136 if (input_position == base::string16::npos) { | 118 if (input_position == base::string16::npos) { |
| 137 // The input text is not a substring of the query string, e.g. input | 119 // The input text is not a substring of the query string, e.g. input |
| 138 // text is "slasdot" and the query string is "slashdot", so we bold the | 120 // text is "slasdot" and the query string is "slashdot", so we bold the |
| 139 // whole thing. | 121 // whole thing. |
| 140 match->contents_class.push_back(ACMatchClassification( | 122 match->contents_class.push_back(ACMatchClassification( |
| 141 match_contents_start, ACMatchClassification::MATCH)); | 123 0, ACMatchClassification::MATCH)); |
| 142 } else { | 124 } else { |
| 143 input_position += match_contents_start; | |
| 144 | |
| 145 // TODO(beng): ACMatchClassification::MATCH now seems to just mean | 125 // TODO(beng): ACMatchClassification::MATCH now seems to just mean |
| 146 // "bold" this. Consider modifying the terminology. | 126 // "bold" this. Consider modifying the terminology. |
| 147 // We don't iterate over the string here annotating all matches because | 127 // We don't iterate over the string here annotating all matches because |
| 148 // it looks odd to have every occurrence of a substring that may be as | 128 // it looks odd to have every occurrence of a substring that may be as |
| 149 // short as a single character highlighted in a query suggestion result, | 129 // short as a single character highlighted in a query suggestion result, |
| 150 // e.g. for input text "s" and query string "southwest airlines", it | 130 // e.g. for input text "s" and query string "southwest airlines", it |
| 151 // looks odd if both the first and last s are highlighted. | 131 // looks odd if both the first and last s are highlighted. |
| 152 if (input_position != match_contents_start) { | 132 if (input_position != 0) { |
| 153 match->contents_class.push_back(ACMatchClassification( | 133 match->contents_class.push_back(ACMatchClassification( |
| 154 match_contents_start, ACMatchClassification::MATCH)); | 134 0, ACMatchClassification::MATCH)); |
| 155 } | 135 } |
| 156 match->contents_class.push_back( | 136 match->contents_class.push_back( |
| 157 ACMatchClassification(input_position, ACMatchClassification::NONE)); | 137 ACMatchClassification(input_position, ACMatchClassification::NONE)); |
| 158 size_t next_fragment_position = input_position + input_text.length(); | 138 size_t next_fragment_position = input_position + input_text.length(); |
| 159 if (next_fragment_position < query_string.length()) { | 139 if (next_fragment_position < query_string.length()) { |
| 160 match->contents_class.push_back(ACMatchClassification( | 140 match->contents_class.push_back(ACMatchClassification( |
| 161 next_fragment_position, ACMatchClassification::MATCH)); | 141 next_fragment_position, ACMatchClassification::MATCH)); |
| 162 } | 142 } |
| 163 } | 143 } |
| 164 } else { | 144 } else { |
| 165 // Otherwise, |match| is a verbatim (what-you-typed) match, either for the | 145 // Otherwise, |match| is a verbatim (what-you-typed) match, either for the |
| 166 // default provider or a keyword search provider. | 146 // default provider or a keyword search provider. |
| 167 match->contents_class.push_back(ACMatchClassification( | 147 match->contents_class.push_back(ACMatchClassification( |
| 168 match_contents_start, ACMatchClassification::NONE)); | 148 0, ACMatchClassification::NONE)); |
| 169 } | 149 } |
| 150 } | |
| 170 | 151 |
| 171 if (!annotation.empty() && (annotation_start >= match_contents_start)) | 152 AutocompleteMatchType::Type GetAutocompleteMatchType(const std::string& type) { |
| 172 match->contents_class.push_back(ACMatchClassification( | 153 if (type == "ENTITY") |
| 173 match_contents_end, ACMatchClassification::DIM)); | 154 return AutocompleteMatchType::SEARCH_SUGGEST_ENTITY; |
| 155 if (type == "INFINITE") | |
| 156 return AutocompleteMatchType::SEARCH_SUGGEST_INFINITE; | |
| 157 if (type == "PERSONALIZED") | |
| 158 return AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED; | |
| 159 if (type == "PROFILE") | |
| 160 return AutocompleteMatchType::SEARCH_SUGGEST_PROFILE; | |
| 161 return AutocompleteMatchType::SEARCH_SUGGEST; | |
| 174 } | 162 } |
| 175 | 163 |
| 176 } // namespace | 164 } // namespace |
| 177 | 165 |
| 178 | 166 |
| 179 // SuggestionDeletionHandler ------------------------------------------------- | 167 // SuggestionDeletionHandler ------------------------------------------------- |
| 180 | 168 |
| 181 // This class handles making requests to the server in order to delete | 169 // This class handles making requests to the server in order to delete |
| 182 // personalized suggestions. | 170 // personalized suggestions. |
| 183 class SuggestionDeletionHandler : public net::URLFetcherDelegate { | 171 class SuggestionDeletionHandler : public net::URLFetcherDelegate { |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 259 } | 247 } |
| 260 | 248 |
| 261 SearchProvider::Result::~Result() { | 249 SearchProvider::Result::~Result() { |
| 262 } | 250 } |
| 263 | 251 |
| 264 | 252 |
| 265 // SearchProvider::SuggestResult ---------------------------------------------- | 253 // SearchProvider::SuggestResult ---------------------------------------------- |
| 266 | 254 |
| 267 SearchProvider::SuggestResult::SuggestResult( | 255 SearchProvider::SuggestResult::SuggestResult( |
| 268 const base::string16& suggestion, | 256 const base::string16& suggestion, |
| 257 AutocompleteMatchType::Type type, | |
| 269 const base::string16& match_contents, | 258 const base::string16& match_contents, |
| 270 const base::string16& annotation, | 259 const base::string16& annotation, |
| 271 const std::string& suggest_query_params, | 260 const std::string& suggest_query_params, |
| 272 const std::string& deletion_url, | 261 const std::string& deletion_url, |
| 273 bool from_keyword_provider, | 262 bool from_keyword_provider, |
| 274 int relevance, | 263 int relevance, |
| 275 bool relevance_from_server, | 264 bool relevance_from_server, |
| 276 bool should_prefetch) | 265 bool should_prefetch) |
| 277 : Result(from_keyword_provider, relevance, relevance_from_server), | 266 : Result(from_keyword_provider, relevance, relevance_from_server), |
| 278 suggestion_(suggestion), | 267 suggestion_(suggestion), |
| 268 type_(type), | |
| 279 match_contents_(match_contents), | 269 match_contents_(match_contents), |
| 280 annotation_(annotation), | 270 annotation_(annotation), |
| 281 suggest_query_params_(suggest_query_params), | 271 suggest_query_params_(suggest_query_params), |
| 282 deletion_url_(deletion_url), | 272 deletion_url_(deletion_url), |
| 283 should_prefetch_(should_prefetch) { | 273 should_prefetch_(should_prefetch) { |
| 284 } | 274 } |
| 285 | 275 |
| 286 SearchProvider::SuggestResult::~SuggestResult() { | 276 SearchProvider::SuggestResult::~SuggestResult() { |
| 287 } | 277 } |
| 288 | 278 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 419 const std::string& suggest_query_params, | 409 const std::string& suggest_query_params, |
| 420 int accepted_suggestion, | 410 int accepted_suggestion, |
| 421 int omnibox_start_margin, | 411 int omnibox_start_margin, |
| 422 bool append_extra_query_params) { | 412 bool append_extra_query_params) { |
| 423 AutocompleteMatch match(autocomplete_provider, relevance, false, type); | 413 AutocompleteMatch match(autocomplete_provider, relevance, false, type); |
| 424 | 414 |
| 425 if (!template_url) | 415 if (!template_url) |
| 426 return match; | 416 return match; |
| 427 match.keyword = template_url->keyword(); | 417 match.keyword = template_url->keyword(); |
| 428 | 418 |
| 429 SetAndClassifyMatchContents( | 419 SetAndClassifyMatchContents(query_string, input_text, match_contents, &match); |
| 430 query_string, input_text, match_contents, annotation, &match); | 420 |
| 421 if (!annotation.empty()) | |
| 422 match.description = annotation; | |
| 431 | 423 |
| 432 match.allowed_to_be_default_match = (input_text == match_contents); | 424 match.allowed_to_be_default_match = (input_text == match_contents); |
| 433 | 425 |
| 434 // When the user forced a query, we need to make sure all the fill_into_edit | 426 // When the user forced a query, we need to make sure all the fill_into_edit |
| 435 // values preserve that property. Otherwise, if the user starts editing a | 427 // values preserve that property. Otherwise, if the user starts editing a |
| 436 // suggestion, non-Search results will suddenly appear. | 428 // suggestion, non-Search results will suddenly appear. |
| 437 if (input.type() == AutocompleteInput::FORCED_QUERY) | 429 if (input.type() == AutocompleteInput::FORCED_QUERY) |
| 438 match.fill_into_edit.assign(ASCIIToUTF16("?")); | 430 match.fill_into_edit.assign(ASCIIToUTF16("?")); |
| 439 if (is_keyword) | 431 if (is_keyword) |
| 440 match.fill_into_edit.append(match.keyword + char16(' ')); | 432 match.fill_into_edit.append(match.keyword + char16(' ')); |
| (...skipping 731 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1172 // Do not blindly trust the URL coming from the server to be valid. | 1164 // Do not blindly trust the URL coming from the server to be valid. |
| 1173 GURL url(URLFixerUpper::FixupURL(UTF16ToUTF8(suggestion), std::string())); | 1165 GURL url(URLFixerUpper::FixupURL(UTF16ToUTF8(suggestion), std::string())); |
| 1174 if (url.is_valid() && allow_navsuggest) { | 1166 if (url.is_valid() && allow_navsuggest) { |
| 1175 base::string16 title; | 1167 base::string16 title; |
| 1176 if (descriptions != NULL) | 1168 if (descriptions != NULL) |
| 1177 descriptions->GetString(index, &title); | 1169 descriptions->GetString(index, &title); |
| 1178 results->navigation_results.push_back(NavigationResult( | 1170 results->navigation_results.push_back(NavigationResult( |
| 1179 *this, url, title, is_keyword, relevance, true)); | 1171 *this, url, title, is_keyword, relevance, true)); |
| 1180 } | 1172 } |
| 1181 } else { | 1173 } else { |
| 1174 AutocompleteMatchType::Type match_type = GetAutocompleteMatchType(type); | |
| 1182 bool should_prefetch = static_cast<int>(index) == prefetch_index; | 1175 bool should_prefetch = static_cast<int>(index) == prefetch_index; |
| 1183 DictionaryValue* suggestion_detail = NULL; | 1176 DictionaryValue* suggestion_detail = NULL; |
| 1184 base::string16 match_contents = suggestion; | 1177 base::string16 match_contents = suggestion; |
| 1185 base::string16 annotation; | 1178 base::string16 annotation; |
| 1186 std::string suggest_query_params; | 1179 std::string suggest_query_params; |
| 1187 std::string deletion_url; | 1180 std::string deletion_url; |
| 1188 | 1181 |
| 1189 if (suggestion_details) { | 1182 if (suggestion_details) { |
| 1190 suggestion_details->GetDictionary(index, &suggestion_detail); | 1183 suggestion_details->GetDictionary(index, &suggestion_detail); |
| 1191 if (suggestion_detail) { | 1184 if (suggestion_detail) { |
| 1192 suggestion_detail->GetString("du", &deletion_url); | 1185 suggestion_detail->GetString("du", &deletion_url); |
| 1193 | 1186 suggestion_detail->GetString("title", &match_contents) || |
| 1194 if (type == "ENTITY") { | 1187 suggestion_detail->GetString("t", &match_contents); |
| 1195 suggestion_detail->GetString("a", &annotation); | 1188 suggestion_detail->GetString("annotation", &annotation) || |
| 1196 | 1189 suggestion_detail->GetString("a", &annotation); |
| 1197 base::string16 disambiguating_query; | 1190 suggestion_detail->GetString("query_params", &suggest_query_params) || |
| 1198 if (suggestion_detail->GetString("dq", &disambiguating_query) && | 1191 suggestion_detail->GetString("q", &suggest_query_params); |
| 1199 !disambiguating_query.empty()) | |
| 1200 suggestion = disambiguating_query; | |
| 1201 | |
| 1202 suggestion_detail->GetString("q", &suggest_query_params); | |
| 1203 } | |
| 1204 } | 1192 } |
| 1205 } | 1193 } |
| 1206 | 1194 |
| 1207 // TODO(kochi): Improve calculator suggestion presentation. | 1195 // TODO(kochi): Improve calculator suggestion presentation. |
| 1208 results->suggest_results.push_back(SuggestResult( | 1196 results->suggest_results.push_back(SuggestResult( |
| 1209 suggestion, match_contents, annotation, suggest_query_params, | 1197 suggestion, match_type, match_contents, annotation, |
| 1210 deletion_url, is_keyword, relevance, true, should_prefetch)); | 1198 suggest_query_params, deletion_url, is_keyword, relevance, true, |
| 1199 should_prefetch)); | |
| 1211 } | 1200 } |
| 1212 } | 1201 } |
| 1213 | 1202 |
| 1214 // Ignore suggested scores for non-keyword matches in keyword mode; if the | 1203 // Ignore suggested scores for non-keyword matches in keyword mode; if the |
| 1215 // server is allowed to score these, it could interfere with the user's | 1204 // server is allowed to score these, it could interfere with the user's |
| 1216 // ability to get good keyword results. | 1205 // ability to get good keyword results. |
| 1217 const bool abandon_suggested_scores = | 1206 const bool abandon_suggested_scores = |
| 1218 !is_keyword && !providers_.keyword_provider().empty(); | 1207 !is_keyword && !providers_.keyword_provider().empty(); |
| 1219 // Apply calculated relevance scores to suggestions if a valid list was | 1208 // Apply calculated relevance scores to suggestions if a valid list was |
| 1220 // not provided or we're abandoning suggested scores entirely. | 1209 // not provided or we're abandoning suggested scores entirely. |
| (...skipping 426 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1647 if (!prevent_inline_autocomplete && classifier && (i->term != input_text)) { | 1636 if (!prevent_inline_autocomplete && classifier && (i->term != input_text)) { |
| 1648 AutocompleteMatch match; | 1637 AutocompleteMatch match; |
| 1649 classifier->Classify(i->term, false, false, &match, NULL); | 1638 classifier->Classify(i->term, false, false, &match, NULL); |
| 1650 prevent_inline_autocomplete = | 1639 prevent_inline_autocomplete = |
| 1651 !AutocompleteMatch::IsSearchType(match.type); | 1640 !AutocompleteMatch::IsSearchType(match.type); |
| 1652 } | 1641 } |
| 1653 | 1642 |
| 1654 int relevance = CalculateRelevanceForHistory( | 1643 int relevance = CalculateRelevanceForHistory( |
| 1655 i->time, is_keyword, !prevent_inline_autocomplete, | 1644 i->time, is_keyword, !prevent_inline_autocomplete, |
| 1656 prevent_search_history_inlining); | 1645 prevent_search_history_inlining); |
| 1657 scored_results.push_back( | 1646 scored_results.push_back(SuggestResult( |
| 1658 SuggestResult(i->term, base::string16(), base::string16(), | 1647 i->term, AutocompleteMatchType::SEARCH_HISTORY, base::string16(), |
| 1659 std::string(), std::string(), is_keyword, relevance, | 1648 base::string16(), std::string(), std::string(), is_keyword, relevance, |
| 1660 false, false)); | 1649 false, false)); |
| 1661 } | 1650 } |
| 1662 | 1651 |
| 1663 // History returns results sorted for us. However, we may have docked some | 1652 // History returns results sorted for us. However, we may have docked some |
| 1664 // results' scores, so things are no longer in order. Do a stable sort to get | 1653 // results' scores, so things are no longer in order. Do a stable sort to get |
| 1665 // things back in order without otherwise disturbing results with equal | 1654 // things back in order without otherwise disturbing results with equal |
| 1666 // scores, then force the scores to be unique, so that the order in which | 1655 // scores, then force the scores to be unique, so that the order in which |
| 1667 // they're shown is deterministic. | 1656 // they're shown is deterministic. |
| 1668 std::stable_sort(scored_results.begin(), scored_results.end(), | 1657 std::stable_sort(scored_results.begin(), scored_results.end(), |
| 1669 CompareScoredResults()); | 1658 CompareScoredResults()); |
| 1670 int last_relevance = 0; | 1659 int last_relevance = 0; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 1683 MatchMap* map) { | 1672 MatchMap* map) { |
| 1684 for (size_t i = 0; i < results.size(); ++i) { | 1673 for (size_t i = 0; i < results.size(); ++i) { |
| 1685 const bool is_keyword = results[i].from_keyword_provider(); | 1674 const bool is_keyword = results[i].from_keyword_provider(); |
| 1686 const base::string16& input = is_keyword ? keyword_input_.text() | 1675 const base::string16& input = is_keyword ? keyword_input_.text() |
| 1687 : input_.text(); | 1676 : input_.text(); |
| 1688 AddMatchToMap(input, | 1677 AddMatchToMap(input, |
| 1689 results[i].relevance(), | 1678 results[i].relevance(), |
| 1690 results[i].relevance_from_server(), | 1679 results[i].relevance_from_server(), |
| 1691 results[i].should_prefetch(), | 1680 results[i].should_prefetch(), |
| 1692 metadata, | 1681 metadata, |
| 1693 AutocompleteMatchType::SEARCH_SUGGEST, | 1682 results[i].type(), |
| 1694 is_keyword, | 1683 is_keyword, |
| 1695 results[i].match_contents(), | 1684 results[i].match_contents(), |
| 1696 results[i].annotation(), | 1685 results[i].annotation(), |
| 1697 results[i].suggestion(), | 1686 results[i].suggestion(), |
| 1698 i, | 1687 i, |
| 1699 results[i].suggest_query_params(), | 1688 results[i].suggest_query_params(), |
| 1700 results[i].deletion_url(), | 1689 results[i].deletion_url(), |
| 1701 map); | 1690 map); |
| 1702 } | 1691 } |
| 1703 } | 1692 } |
| (...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2084 if (!OmniboxFieldTrial::InZeroSuggestFieldTrial() || | 2073 if (!OmniboxFieldTrial::InZeroSuggestFieldTrial() || |
| 2085 service == NULL || | 2074 service == NULL || |
| 2086 !service->IsSyncEnabledAndLoggedIn() || | 2075 !service->IsSyncEnabledAndLoggedIn() || |
| 2087 !sync_prefs.GetPreferredDataTypes(syncer::UserTypes()).Has( | 2076 !sync_prefs.GetPreferredDataTypes(syncer::UserTypes()).Has( |
| 2088 syncer::PROXY_TABS) || | 2077 syncer::PROXY_TABS) || |
| 2089 service->GetEncryptedDataTypes().Has(syncer::SESSIONS)) | 2078 service->GetEncryptedDataTypes().Has(syncer::SESSIONS)) |
| 2090 return false; | 2079 return false; |
| 2091 | 2080 |
| 2092 return true; | 2081 return true; |
| 2093 } | 2082 } |
| OLD | NEW |