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 802b879de05b6d5c94107609cc2a4dc934d5a0d2..4fb62b14d2a6a6698e79626a0b57b9eede489b56 100644 |
| --- a/chrome/browser/autocomplete/search_provider.cc |
| +++ b/chrome/browser/autocomplete/search_provider.cc |
| @@ -170,6 +170,33 @@ int SearchProvider::CalculateRelevanceForKeywordVerbatim( |
| return (type == metrics::OmniboxInputType::QUERY) ? 1450 : 1100; |
| } |
| +// static |
| +void SearchProvider::RemoveOrReviseOldResultsForOneProvider( |
| + bool minimal_changes, SearchSuggestionParser::Results* results) { |
| + // When called without |minimal_changes|, it likely means the user has |
| + // pressed a key. Revise the cached results appropriately. |
| + if (!minimal_changes) { |
| + for (SearchSuggestionParser::SuggestResults::iterator sug_it = |
| + results->suggest_results.begin(); |
| + sug_it != results->suggest_results.end(); ++sug_it) { |
| + sug_it->set_received_after_last_keystroke(false); |
| + } |
| + for (SearchSuggestionParser::NavigationResults::iterator nav_it = |
| + results->navigation_results.begin(); |
| + nav_it != results->navigation_results.end(); ++nav_it) { |
| + nav_it->set_received_after_last_keystroke(false); |
| + } |
| + } |
| +} |
| + |
| +// static |
| +ACMatches::iterator SearchProvider::FindTopMatch(ACMatches* matches) { |
| + ACMatches::iterator it = matches->begin(); |
| + while ((it != matches->end()) && !it->allowed_to_be_default_match) |
| + ++it; |
| + return it; |
| +} |
| + |
| void SearchProvider::Start(const AutocompleteInput& input, |
| bool minimal_changes) { |
| // Do our best to load the model as early as possible. This will reduce |
| @@ -392,6 +419,8 @@ void SearchProvider::LogFetchComplete(bool success, bool is_keyword) { |
| } |
| void SearchProvider::UpdateMatches() { |
| + PersistGoodResultsForOneProvider(&default_results_); |
| + PersistGoodResultsForOneProvider(&keyword_results_); |
| ConvertResultsToAutocompleteMatches(); |
| // Check constraints that may be violated by suggested relevances. |
| @@ -401,11 +430,12 @@ void SearchProvider::UpdateMatches() { |
| // These blocks attempt to repair undesirable behavior by suggested |
| // relevances with minimal impact, preserving other suggested relevances. |
| - if (!HasKeywordDefaultMatchInKeywordMode()) { |
| + if ((providers_.GetKeywordProviderURL() != NULL) && |
| + (FindTopMatch() == matches_.end())) { |
| // In keyword mode, disregard the keyword verbatim suggested relevance |
| - // if necessary so there at least one keyword match that's allowed to |
| - // be the default match. |
| - keyword_results_.verbatim_relevance = -1; |
| + // if necessary, so at least one match is allowed to be default. Give |
| + // it the lowest non-zero score to best reflect what the server desired. |
| + keyword_results_.verbatim_relevance = 1; |
| ConvertResultsToAutocompleteMatches(); |
| } |
| if (IsTopMatchSearchWithURLInput()) { |
| @@ -422,28 +452,34 @@ void SearchProvider::UpdateMatches() { |
| if (FindTopMatch() == matches_.end()) { |
| // Guarantee that SearchProvider returns a legal default match. (The |
| // omnibox always needs at least one legal default match, and it relies |
| - // on SearchProvider to always return one.) |
| - ApplyCalculatedRelevance(); |
| + // on SearchProvider to always return one.) Give them the lowest |
| + // non-zero scores to best reflect what the server desired. |
| + default_results_.verbatim_relevance = 1; |
| + keyword_results_.verbatim_relevance = 1; |
| ConvertResultsToAutocompleteMatches(); |
| } |
| - DCHECK(HasKeywordDefaultMatchInKeywordMode()); |
| DCHECK(!IsTopMatchSearchWithURLInput()); |
| DCHECK(FindTopMatch() != matches_.end()); |
| } |
| UMA_HISTOGRAM_CUSTOM_COUNTS( |
| "Omnibox.SearchProviderMatches", matches_.size(), 1, 6, 7); |
| - const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
|
Mark P
2014/08/15 20:04:38
This was clobbered in the refactoring changelist.
|
| - if ((keyword_url != NULL) && HasKeywordDefaultMatchInKeywordMode()) { |
| - // If there is a keyword match that is allowed to be the default match, |
| - // then prohibit default provider matches from being the default match lest |
| - // such matches cause the user to break out of keyword mode. |
| - for (ACMatches::iterator it = matches_.begin(); it != matches_.end(); |
| - ++it) { |
| - if (it->keyword != keyword_url->keyword()) |
| - it->allowed_to_be_default_match = false; |
| + // Record the inlined suggestion (if any) for future use. |
| + inlined_query_suggestion_match_contents_ = base::string16(); |
| + inlined_navsuggestion_ = GURL(); |
| + ACMatches::const_iterator first_match = FindTopMatch(); |
| + if ((first_match != matches_.end()) && |
| + !first_match->inline_autocompletion.empty()) { |
| + // Identify if this match came from a query suggestion or a navsuggestion. |
| + // In either case, extracts the identifying feature of the suggestion |
| + // (query string or navigation url). |
| + if (AutocompleteMatch::IsSearchType(first_match->type)) { |
| + inlined_query_suggestion_match_contents_ = first_match->contents; |
| + } else { |
| + inlined_navsuggestion_ = first_match->destination_url; |
| } |
| } |
| + |
| UpdateDone(); |
| } |
| @@ -534,8 +570,7 @@ void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { |
| // We can't keep running any previous query, so halt it. |
| StopSuggest(); |
| - // Remove existing results that cannot inline autocomplete the new input. |
| - RemoveAllStaleResults(); |
| + RemoveOrReviseOldResults(minimal_changes); |
| // Update the content classifications of remaining results so they look good |
| // against the current input. |
| @@ -619,21 +654,39 @@ bool SearchProvider::IsQuerySuitableForSuggest() const { |
| return true; |
| } |
| -void SearchProvider::RemoveAllStaleResults() { |
| +void SearchProvider::RemoveOrReviseOldResults(bool minimal_changes) { |
| if (keyword_input_.text().empty()) { |
| // User is either in keyword mode with a blank input or out of |
| // keyword mode entirely. |
| keyword_results_.Clear(); |
| } |
| + RemoveOrReviseOldResultsForOneProvider(minimal_changes, &default_results_); |
| + RemoveOrReviseOldResultsForOneProvider(minimal_changes, &keyword_results_); |
| } |
| -void SearchProvider::ApplyCalculatedRelevance() { |
| - ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results); |
| - ApplyCalculatedSuggestRelevance(&default_results_.suggest_results); |
| - ApplyCalculatedNavigationRelevance(&keyword_results_.navigation_results); |
| - ApplyCalculatedNavigationRelevance(&default_results_.navigation_results); |
| - default_results_.verbatim_relevance = -1; |
| - keyword_results_.verbatim_relevance = -1; |
| +void SearchProvider::PersistGoodResultsForOneProvider( |
|
msw
2014/08/15 21:04:37
Why do we need this at all? Isn't "RemoveOrReviseO
Mark P
2014/08/15 22:09:21
This is definitely needed. Without this function
msw
2014/08/15 23:31:10
The new name is good, thanks for explaining the ne
|
| + SearchSuggestionParser::Results* results) { |
| + // Mark the result corresponding to the previous inline autocompletion as |
| + // having been received on a previous keystroke (as indeed it must have |
|
msw
2014/08/15 21:04:37
nit: s/on a previous/before the last/
Mark P
2014/08/15 22:09:21
Done.
|
| + // because we prohibit newly-received results from becoming the inline |
|
msw
2014/08/15 21:04:37
I don't know how this (indeed it must have...) com
Mark P
2014/08/15 22:09:21
Removed the comment. I think the comment by the f
|
| + // autocompletion). |
| + if (!inlined_query_suggestion_match_contents_.empty()) { |
| + for (SearchSuggestionParser::SuggestResults::iterator sug_it = |
| + results->suggest_results.begin(); |
| + sug_it != results->suggest_results.end(); ++sug_it) { |
| + if (sug_it->match_contents() == |
| + inlined_query_suggestion_match_contents_) |
| + sug_it->set_received_after_last_keystroke(false); |
| + } |
| + } |
| + if (inlined_navsuggestion_.is_valid()) { |
| + for (SearchSuggestionParser::NavigationResults::iterator nav_it = |
| + results->navigation_results.begin(); |
| + nav_it != results->navigation_results.end(); ++nav_it) { |
| + if (nav_it->url() == inlined_navsuggestion_) |
| + nav_it->set_received_after_last_keystroke(false); |
| + } |
| + } |
| } |
| void SearchProvider::ApplyCalculatedSuggestRelevance( |
| @@ -730,6 +783,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| default_results_.suggest_results.empty() ? |
| TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : |
| TemplateURLRef::NO_SUGGESTION_CHOSEN; |
| + const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
| if (verbatim_relevance > 0) { |
| const base::string16& trimmed_verbatim = |
| base::CollapseWhitespace(input_.text(), false); |
| @@ -755,10 +809,9 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| answer_type, std::string(), std::string(), false, verbatim_relevance, |
| relevance_from_server, false, trimmed_verbatim); |
| AddMatchToMap(verbatim, std::string(), did_not_accept_default_suggestion, |
| - false, &map); |
| + false, keyword_url != NULL, &map); |
| } |
| if (!keyword_input_.text().empty()) { |
| - const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
| // We only create the verbatim search query match for a keyword |
| // if it's not an extension keyword. Extension keywords are handled |
| // in KeywordProvider::Start(). (Extensions are complicated...) |
| @@ -780,7 +833,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| true, keyword_verbatim_relevance, keyword_relevance_from_server, |
| false, trimmed_verbatim); |
| AddMatchToMap(verbatim, std::string(), |
| - did_not_accept_keyword_suggestion, false, &map); |
| + did_not_accept_keyword_suggestion, false, true, &map); |
| } |
| } |
| } |
| @@ -802,9 +855,10 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| AddNavigationResultsToMatches(default_results_.navigation_results, &matches); |
| // Now add the most relevant matches to |matches_|. We take up to kMaxMatches |
| - // suggest/navsuggest matches, regardless of origin. If Instant Extended is |
| - // enabled and we have server-provided (and thus hopefully more accurate) |
| - // scores for some suggestions, we allow more of those, until we reach |
| + // suggest/navsuggest matches, regardless of origin. We always include in |
| + // that set a legal default match if possible. If Instant Extended is enabled |
| + // and we have server-provided (and thus hopefully more accurate) scores for |
| + // some suggestions, we allow more of those, until we reach |
| // AutocompleteResult::kMaxMatches total matches (that is, enough to fill the |
| // whole popup). |
| // |
| @@ -813,6 +867,13 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
| // higher-scoring matches under the conditions above. |
| std::sort(matches.begin(), matches.end(), &AutocompleteMatch::MoreRelevant); |
| matches_.clear(); |
| + // Guarantee that if there's a legal default match anywhere in the result |
|
msw
2014/08/15 21:04:37
Why is this needed now? I thought the |allowed_to_
Mark P
2014/08/15 22:09:21
This is needed because AutocompleteResult enforces
msw
2014/08/15 23:31:10
My question is "why do we need to rotate the match
Mark P
2014/08/15 23:50:15
This call to rotate does a remove from current loc
|
| + // set that it'll get returned by moving the default match to the front |
| + // of the list. |
| + ACMatches::iterator default_match = FindTopMatch(&matches); |
| + if (default_match != matches.end()) { |
|
msw
2014/08/15 21:04:37
nit: remove curly braces.
Mark P
2014/08/15 22:09:21
Done.
|
| + std::rotate(matches.begin(), default_match, default_match + 1); |
| + } |
| size_t num_suggestions = 0; |
| for (ACMatches::const_iterator i(matches.begin()); |
| @@ -848,21 +909,6 @@ ACMatches::const_iterator SearchProvider::FindTopMatch() const { |
| return it; |
| } |
| -bool SearchProvider::HasKeywordDefaultMatchInKeywordMode() const { |
|
Mark P
2014/08/15 20:04:38
Deleted in refactoring changelist.
|
| - const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
| - // If the user is not in keyword mode, return true to say that this |
| - // constraint is not violated. |
| - if (keyword_url == NULL) |
| - return true; |
| - for (ACMatches::const_iterator it = matches_.begin(); it != matches_.end(); |
| - ++it) { |
| - if ((it->keyword == keyword_url->keyword()) && |
| - it->allowed_to_be_default_match) |
| - return true; |
| - } |
| - return false; |
| -} |
| - |
| bool SearchProvider::IsTopMatchSearchWithURLInput() const { |
| ACMatches::const_iterator first_match = FindTopMatch(); |
| return (input_.type() == metrics::OmniboxInputType::URL) && |
| @@ -922,7 +968,8 @@ void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, |
| is_keyword); |
| for (SearchSuggestionParser::SuggestResults::const_iterator i( |
| scored_results.begin()); i != scored_results.end(); ++i) { |
| - AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true, map); |
| + AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true, |
| + providers_.GetKeywordProviderURL() != NULL, map); |
| } |
| UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime", |
| base::TimeTicks::Now() - start_time); |
| @@ -965,13 +1012,15 @@ SearchSuggestionParser::SuggestResults SearchProvider::ScoreHistoryResults( |
| found_what_you_typed_match = true; |
| insertion_position = scored_results.begin(); |
| } |
| - scored_results.insert( |
| - insertion_position, |
| - SearchSuggestionParser::SuggestResult( |
| - trimmed_suggestion, AutocompleteMatchType::SEARCH_HISTORY, |
| - trimmed_suggestion, base::string16(), base::string16(), |
| - base::string16(), base::string16(), std::string(), std::string(), |
| - is_keyword, relevance, false, false, trimmed_input)); |
| + SearchSuggestionParser::SuggestResult history_suggestion( |
| + trimmed_suggestion, AutocompleteMatchType::SEARCH_HISTORY, |
| + trimmed_suggestion, base::string16(), base::string16(), |
| + base::string16(), base::string16(), std::string(), std::string(), |
| + is_keyword, relevance, false, false, trimmed_input); |
| + // History results are synchronous; they are received at the last keystroke, |
|
msw
2014/08/15 21:04:37
nit: s/upon/with/ remove ", not after it"
Mark P
2014/08/15 22:09:21
Did the latter.
The former fails to apply. Ther
msw
2014/08/15 23:31:10
I meant to encourage "upon the last keystroke", bu
Mark P
2014/08/15 23:50:15
Switched to "upon".
|
| + // not after it. |
| + history_suggestion.set_received_after_last_keystroke(false); |
| + scored_results.insert(insertion_position, history_suggestion); |
| } |
| // History returns results sorted for us. However, we may have docked some |
| @@ -1044,8 +1093,10 @@ void SearchProvider::AddSuggestResultsToMap( |
| const SearchSuggestionParser::SuggestResults& results, |
| const std::string& metadata, |
| MatchMap* map) { |
| - for (size_t i = 0; i < results.size(); ++i) |
| - AddMatchToMap(results[i], metadata, i, false, map); |
| + for (size_t i = 0; i < results.size(); ++i) { |
| + AddMatchToMap(results[i], metadata, i, false, |
| + providers_.GetKeywordProviderURL() != NULL, map); |
| + } |
| } |
| int SearchProvider::GetVerbatimRelevance(bool* relevance_from_server) const { |
| @@ -1204,14 +1255,18 @@ AutocompleteMatch SearchProvider::NavigationToMatch( |
| } |
| // An inlineable navsuggestion can only be the default match when there |
| // is no keyword provider active, lest it appear first and break the user |
| - // out of keyword mode. It can also only be default if either the inline |
| + // out of keyword mode. We also must have received the navsuggestion before |
| + // the last keystroke. (We don't want asynchronous inline autocompletions.) |
| + // The navsuggestion can also only be default if either the inline |
| // autocompletion is empty or we're not preventing inline autocompletion. |
| // Finally, if we have an inlineable navsuggestion with an inline completion |
| // that we're not preventing, make sure we didn't trim any whitespace. |
| // We don't want to claim http://foo.com/bar is inlineable against the |
| // input "foo.com/b ". |
| - match.allowed_to_be_default_match = (prefix != NULL) && |
| + match.allowed_to_be_default_match = |
| + (prefix != NULL) && |
| (providers_.GetKeywordProviderURL() == NULL) && |
| + !navigation.received_after_last_keystroke() && |
| (match.inline_autocompletion.empty() || |
| (!input_.prevent_inline_autocomplete() && !trimmed_whitespace)); |
| match.EnsureUWYTIsAllowedToBeDefault( |