Chromium Code Reviews| Index: chrome/browser/autocomplete/search_provider.cc |
| diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc |
| index c561490ad1301b5468d61b353253b4df8253852d..f3177907132d62702a393154909df72437d1b8e9 100644 |
| --- a/chrome/browser/autocomplete/search_provider.cc |
| +++ b/chrome/browser/autocomplete/search_provider.cc |
| @@ -133,12 +133,55 @@ SearchProvider::Result::~Result() { |
| // SearchProvider::SuggestResult ---------------------------------------------- |
| -SearchProvider::SuggestResult::SuggestResult(const string16& suggestion, |
| - bool from_keyword_provider, |
| - int relevance, |
| - bool relevance_from_server) |
| +SearchProvider::SuggestResult::SuggestResult( |
| + const string16& suggestion, |
| + bool from_keyword_provider, |
| + int relevance, |
| + bool relevance_from_server, |
| + const string16& input_text) |
| : Result(from_keyword_provider, relevance, relevance_from_server), |
| suggestion_(suggestion) { |
| + contents_.assign(suggestion_); |
|
Mark P
2013/08/15 18:34:23
This whole blob was moved from CreateSearchSuggest
|
| + contents_class_.clear(); |
| + // We do intra-string highlighting for suggestions - the suggested segment |
| + // will be highlighted, e.g. for input_text = "you" the suggestion may be |
| + // "youtube", so we'll bold the "tube" section: you*tube*. |
| + if (input_text != suggestion_) { |
| + size_t input_position = contents_.find(input_text); |
| + if (input_position == string16::npos) { |
| + // The input text is not a substring of the query string, e.g. input |
| + // text is "slasdot" and the query string is "slashdot", so we bold the |
| + // whole thing. |
| + contents_class_.push_back( |
| + ACMatchClassification(0, ACMatchClassification::MATCH)); |
| + } else { |
| + // TODO(beng): ACMatchClassification::MATCH now seems to just mean |
| + // "bold" this. Consider modifying the terminology. |
| + // We don't iterate over the string here annotating all matches because |
| + // it looks odd to have every occurrence of a substring that may be as |
| + // short as a single character highlighted in a query suggestion result, |
| + // e.g. for input text "s" and query string "southwest airlines", it |
| + // looks odd if both the first and last s are highlighted. |
| + if (input_position != 0) { |
| + contents_class_.push_back( |
| + ACMatchClassification(0, ACMatchClassification::MATCH)); |
| + } |
| + contents_class_.push_back( |
| + ACMatchClassification(input_position, ACMatchClassification::NONE)); |
| + size_t next_fragment_position = input_position + input_text.length(); |
| + if (next_fragment_position < suggestion_.length()) { |
| + contents_class_.push_back( |
| + ACMatchClassification(next_fragment_position, |
| + ACMatchClassification::MATCH)); |
| + } |
| + } |
| + } else { |
| + // Otherwise, |match| is a verbatim (what-you-typed) match, either for the |
| + // default provider or a keyword search provider. |
| + contents_class_.push_back( |
| + ACMatchClassification(0, ACMatchClassification::NONE)); |
| + |
| + } |
| } |
| SearchProvider::SuggestResult::~SuggestResult() { |
| @@ -165,13 +208,37 @@ SearchProvider::NavigationResult::NavigationResult( |
| const string16& description, |
| bool from_keyword_provider, |
| int relevance, |
| - bool relevance_from_server) |
| + bool relevance_from_server, |
| + const string16& input_text, |
| + const std::string& languages) |
| : Result(from_keyword_provider, relevance, relevance_from_server), |
| url_(url), |
| formatted_url_(AutocompleteInput::FormattedStringWithEquivalentMeaning( |
| url, provider.StringForURLDisplay(url, true, false))), |
| description_(description) { |
| DCHECK(url_.is_valid()); |
| + // First look for the user's input inside the fill_into_edit as it would be |
|
Mark P
2013/08/15 18:34:23
This whole section is the necessary parts from Nav
msw
2013/08/15 18:51:03
Can we reduce more duplication between the two?
Mark P
2013/08/15 19:28:15
I can't figure out how. Here's the problem: inlin
msw
2013/08/15 19:48:30
I was thinking of a helper function, but only if w
|
| + // without trimming the scheme, so we can find matches at the beginning of the |
| + // scheme. |
| + const string16& untrimmed_fill_into_edit = formatted_url_; |
| + const URLPrefix* prefix = |
| + URLPrefix::BestURLPrefix(untrimmed_fill_into_edit, input_text); |
| + size_t match_start = (prefix == NULL) ? |
| + untrimmed_fill_into_edit.find(input_text) : prefix->prefix.length(); |
| + bool trim_http = !HasHTTPScheme(input_text) && (!prefix || (match_start != 0)); |
| + const net::FormatUrlTypes format_types = |
| + net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP); |
| + contents_ = net::FormatUrl(url_, languages, |
| + format_types, net::UnescapeRule::SPACES, NULL, NULL, &match_start); |
| + // If the first match in the untrimmed string was inside a scheme that we |
| + // trimmed, look for a subsequent match. |
| + if (match_start == string16::npos) |
| + match_start = contents_.find(input_text); |
| + // Safe if |match_start| is npos; also safe if the input is longer than the |
| + // remaining contents after |match_start|. |
| + AutocompleteMatch::ClassifyLocationInString(match_start, input_text.length(), |
| + contents_.length(), ACMatchClassification::URL, |
| + &contents_class_); |
| } |
| SearchProvider::NavigationResult::~NavigationResult() { |
| @@ -259,81 +326,45 @@ SearchProvider::SearchProvider(AutocompleteProviderListener* listener, |
| // static |
| AutocompleteMatch SearchProvider::CreateSearchSuggestion( |
| AutocompleteProvider* autocomplete_provider, |
| - int relevance, |
| + const SuggestResult& result, |
| AutocompleteMatch::Type type, |
| const TemplateURL* template_url, |
| - const string16& query_string, |
| const string16& input_text, |
| const AutocompleteInput& input, |
| - bool is_keyword, |
| int accepted_suggestion, |
| int omnibox_start_margin, |
| bool append_extra_query_params) { |
| - AutocompleteMatch match(autocomplete_provider, relevance, false, type); |
| + AutocompleteMatch match( |
| + autocomplete_provider, result.relevance(), false, type); |
| if (!template_url) |
| return match; |
| match.keyword = template_url->keyword(); |
| - match.contents.assign(query_string); |
| - // We do intra-string highlighting for suggestions - the suggested segment |
| - // will be highlighted, e.g. for input_text = "you" the suggestion may be |
| - // "youtube", so we'll bold the "tube" section: you*tube*. |
| - if (input_text != query_string) { |
| - size_t input_position = match.contents.find(input_text); |
| - if (input_position == string16::npos) { |
| - // The input text is not a substring of the query string, e.g. input |
| - // text is "slasdot" and the query string is "slashdot", so we bold the |
| - // whole thing. |
| - match.contents_class.push_back( |
| - ACMatchClassification(0, ACMatchClassification::MATCH)); |
| - } else { |
| - // TODO(beng): ACMatchClassification::MATCH now seems to just mean |
| - // "bold" this. Consider modifying the terminology. |
| - // We don't iterate over the string here annotating all matches because |
| - // it looks odd to have every occurrence of a substring that may be as |
| - // short as a single character highlighted in a query suggestion result, |
| - // e.g. for input text "s" and query string "southwest airlines", it |
| - // looks odd if both the first and last s are highlighted. |
| - if (input_position != 0) { |
| - match.contents_class.push_back( |
| - ACMatchClassification(0, ACMatchClassification::MATCH)); |
| - } |
| - match.contents_class.push_back( |
| - ACMatchClassification(input_position, ACMatchClassification::NONE)); |
| - size_t next_fragment_position = input_position + input_text.length(); |
| - if (next_fragment_position < query_string.length()) { |
| - match.contents_class.push_back( |
| - ACMatchClassification(next_fragment_position, |
| - ACMatchClassification::MATCH)); |
| - } |
| - } |
| - } else { |
| - // Otherwise, |match| is a verbatim (what-you-typed) match, either for the |
| - // default provider or a keyword search provider. |
| - match.contents_class.push_back( |
| - ACMatchClassification(0, ACMatchClassification::NONE)); |
| + match.contents = result.contents(); |
| + match.contents_class = result.contents_class(); |
| + if (input_text == result.suggestion()) |
| match.allowed_to_be_default_match = true; |
| - } |
| // When the user forced a query, we need to make sure all the fill_into_edit |
| // values preserve that property. Otherwise, if the user starts editing a |
| // suggestion, non-Search results will suddenly appear. |
| if (input.type() == AutocompleteInput::FORCED_QUERY) |
| match.fill_into_edit.assign(ASCIIToUTF16("?")); |
| - if (is_keyword) |
| + if (result.from_keyword_provider()) |
| match.fill_into_edit.append(match.keyword + char16(' ')); |
| if (!input.prevent_inline_autocomplete() && |
| - StartsWith(query_string, input_text, false)) { |
| - match.inline_autocompletion = query_string.substr(input_text.length()); |
| + StartsWith(result.suggestion(), input_text, false)) { |
| + match.inline_autocompletion = |
| + result.suggestion().substr(input_text.length()); |
| match.allowed_to_be_default_match = true; |
| } |
| - match.fill_into_edit.append(query_string); |
| + match.fill_into_edit.append(result.suggestion()); |
| const TemplateURLRef& search_url = template_url->url_ref(); |
| DCHECK(search_url.SupportsReplacement()); |
| match.search_terms_args.reset( |
| - new TemplateURLRef::SearchTermsArgs(query_string)); |
| + new TemplateURLRef::SearchTermsArgs(result.suggestion())); |
| match.search_terms_args->original_query = input_text; |
| match.search_terms_args->accepted_suggestion = accepted_suggestion; |
| match.search_terms_args->omnibox_start_margin = omnibox_start_margin; |
| @@ -346,7 +377,7 @@ AutocompleteMatch SearchProvider::CreateSearchSuggestion( |
| GURL(search_url.ReplaceSearchTerms(*match.search_terms_args.get())); |
| // Search results don't look like URLs. |
| - match.transition = is_keyword ? |
| + match.transition = result.from_keyword_provider() ? |
| content::PAGE_TRANSITION_KEYWORD : content::PAGE_TRANSITION_GENERATED; |
| return match; |
| @@ -906,6 +937,8 @@ bool SearchProvider::ParseSuggestResults(Value* root_val, bool is_keyword) { |
| string16 result, title; |
| std::string type; |
| int relevance = -1; |
| + const std::string& languages = |
| + profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); |
| for (size_t index = 0; results_list->GetString(index, &result); ++index) { |
| // Google search may return empty suggestions for weird input characters, |
| // they make no sense at all and can cause problems in our code. |
| @@ -915,6 +948,8 @@ bool SearchProvider::ParseSuggestResults(Value* root_val, bool is_keyword) { |
| // Apply valid suggested relevance scores; discard invalid lists. |
| if (relevances != NULL && !relevances->GetInteger(index, &relevance)) |
| relevances = NULL; |
| + string16 contents; |
| + ACMatchClassifications contents_class; |
|
Mark P
2013/08/15 18:34:23
Forgot to delete those two lines, sorry.
|
| if (types && types->GetString(index, &type) && (type == "NAVIGATION")) { |
| // Do not blindly trust the URL coming from the server to be valid. |
| GURL url(URLFixerUpper::FixupURL(UTF16ToUTF8(result), std::string())); |
| @@ -922,12 +957,12 @@ bool SearchProvider::ParseSuggestResults(Value* root_val, bool is_keyword) { |
| if (descriptions != NULL) |
| descriptions->GetString(index, &title); |
| results->navigation_results.push_back(NavigationResult( |
| - *this, url, title, is_keyword, relevance, true)); |
| + *this, url, title, is_keyword, relevance, true, input_text, languages)); |
| } |
| } else { |
| // TODO(kochi): Improve calculator result presentation. |
| results->suggest_results.push_back( |
| - SuggestResult(result, is_keyword, relevance, true)); |
| + SuggestResult(result, is_keyword, relevance, true, input_text)); |
| } |
| } |
| @@ -964,10 +999,11 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : |
| TemplateURLRef::NO_SUGGESTION_CHOSEN; |
| if (verbatim_relevance > 0) { |
| - AddMatchToMap(input_.text(), input_.text(), verbatim_relevance, |
| - relevance_from_server, |
| + SuggestResult verbatim( |
| + input_.text(), false, verbatim_relevance, relevance_from_server, input_.text()); |
| + AddMatchToMap(verbatim, input_.text(), |
| AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, |
| - did_not_accept_default_suggestion, false, &map); |
| + did_not_accept_default_suggestion, &map); |
| } |
| if (!keyword_input_.text().empty()) { |
| const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
| @@ -982,10 +1018,12 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| const int keyword_verbatim_relevance = |
| GetKeywordVerbatimRelevance(&keyword_relevance_from_server); |
| if (keyword_verbatim_relevance > 0) { |
| - AddMatchToMap(keyword_input_.text(), keyword_input_.text(), |
| - keyword_verbatim_relevance, keyword_relevance_from_server, |
| + SuggestResult keyword_verbatim( |
| + keyword_input_.text(), true, keyword_verbatim_relevance, |
| + keyword_relevance_from_server, keyword_input_.text()); |
| + AddMatchToMap(keyword_verbatim, keyword_input_.text(), |
| AutocompleteMatchType::SEARCH_OTHER_ENGINE, |
| - did_not_accept_keyword_suggestion, true, &map); |
| + did_not_accept_keyword_suggestion, &map); |
| } |
| } |
| } |
| @@ -1208,10 +1246,9 @@ void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, |
| is_keyword); |
| for (SuggestResults::const_iterator i(scored_results.begin()); |
| i != scored_results.end(); ++i) { |
| - AddMatchToMap(i->suggestion(), input_text, i->relevance(), false, |
| + AddMatchToMap(*i, input_text, |
| AutocompleteMatchType::SEARCH_HISTORY, |
| - did_not_accept_suggestion, |
| - is_keyword, map); |
| + did_not_accept_suggestion, map); |
| } |
| } |
| @@ -1258,7 +1295,7 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( |
| i->time, is_keyword, !prevent_inline_autocomplete, |
| prevent_search_history_inlining); |
| scored_results.push_back( |
| - SuggestResult(i->term, is_keyword, relevance, false)); |
| + SuggestResult(i->term, is_keyword, relevance, false, input_text)); |
| } |
| // History returns results sorted for us. However, we may have docked some |
| @@ -1282,11 +1319,10 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( |
| void SearchProvider::AddSuggestResultsToMap(const SuggestResults& results, |
| MatchMap* map) { |
| for (size_t i = 0; i < results.size(); ++i) { |
| - const bool is_keyword = results[i].from_keyword_provider(); |
| - const string16& input = is_keyword ? keyword_input_.text() : input_.text(); |
| - AddMatchToMap(results[i].suggestion(), input, results[i].relevance(), |
| - results[i].relevance_from_server(), |
| - AutocompleteMatchType::SEARCH_SUGGEST, i, is_keyword, map); |
| + const string16& input = results[i].from_keyword_provider() ? |
| + keyword_input_.text() : input_.text(); |
| + AddMatchToMap(results[i], input, |
| + AutocompleteMatchType::SEARCH_SUGGEST, i, map); |
| } |
| } |
| @@ -1398,13 +1434,10 @@ int SearchProvider::CalculateRelevanceForHistory( |
| return std::max(0, base_score - score_discount); |
| } |
| -void SearchProvider::AddMatchToMap(const string16& query_string, |
| +void SearchProvider::AddMatchToMap(const SuggestResult& result, |
| const string16& input_text, |
| - int relevance, |
| - bool relevance_from_server, |
| AutocompleteMatch::Type type, |
| int accepted_suggestion, |
| - bool is_keyword, |
| MatchMap* map) { |
| // On non-mobile, ask the instant controller for the appropriate start margin. |
| // On mobile the start margin is unused, so leave the value as default there. |
| @@ -1421,22 +1454,22 @@ void SearchProvider::AddMatchToMap(const string16& query_string, |
| } |
| #endif // !defined(OS_ANDROID) && !defined(IOS) |
| - const TemplateURL* template_url = is_keyword ? |
| + const TemplateURL* template_url = result.from_keyword_provider() ? |
| providers_.GetKeywordProviderURL() : providers_.GetDefaultProviderURL(); |
| - AutocompleteMatch match = CreateSearchSuggestion(this, relevance, type, |
| - template_url, query_string, input_text, input_, is_keyword, |
| - accepted_suggestion, omnibox_start_margin, |
| - !is_keyword || providers_.default_provider().empty()); |
| + AutocompleteMatch match = CreateSearchSuggestion(this, result, |
| + type, template_url, input_text, input_, accepted_suggestion, |
| + omnibox_start_margin, |
| + !result.from_keyword_provider() || providers_.default_provider().empty()); |
| if (!match.destination_url.is_valid()) |
| return; |
| match.RecordAdditionalInfo(kRelevanceFromServerKey, |
| - relevance_from_server ? kTrue : kFalse); |
| + result.relevance_from_server() ? kTrue : kFalse); |
| // Try to add |match| to |map|. If a match for |query_string| is already in |
| // |map|, replace it if |match| is more relevant. |
| // NOTE: Keep this ToLower() call in sync with url_database.cc. |
| - const std::pair<MatchMap::iterator, bool> i( |
| - map->insert(std::make_pair(base::i18n::ToLower(query_string), match))); |
| + const std::pair<MatchMap::iterator, bool> i(map->insert(std::make_pair( |
| + base::i18n::ToLower(result.suggestion()), match))); |
| // NOTE: We purposefully do a direct relevance comparison here instead of |
| // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added |
| // first" rather than "items alphabetically first" when the scores are equal. |
| @@ -1494,18 +1527,8 @@ AutocompleteMatch SearchProvider::NavigationToMatch( |
| match.fill_into_edit.substr(inline_autocomplete_offset); |
| } |
| - match.contents = net::FormatUrl(navigation.url(), languages, |
| - format_types, net::UnescapeRule::SPACES, NULL, NULL, &match_start); |
| - // If the first match in the untrimmed string was inside a scheme that we |
| - // trimmed, look for a subsequent match. |
| - if (match_start == string16::npos) |
| - match_start = match.contents.find(input); |
| - // Safe if |match_start| is npos; also safe if the input is longer than the |
| - // remaining contents after |match_start|. |
| - AutocompleteMatch::ClassifyLocationInString(match_start, input.length(), |
| - match.contents.length(), ACMatchClassification::URL, |
| - &match.contents_class); |
| - |
| + match.contents = navigation.contents(); |
| + match.contents_class = navigation.contents_class(); |
| match.description = navigation.description(); |
| AutocompleteMatch::ClassifyMatchInString(input, match.description, |
| ACMatchClassification::NONE, &match.description_class); |