OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 | 84 |
85 string16 adjusted_input_text(input_text); | 85 string16 adjusted_input_text(input_text); |
86 AutocompleteInput::RemoveForcedQueryStringIfNecessary(input_.type(), | 86 AutocompleteInput::RemoveForcedQueryStringIfNecessary(input_.type(), |
87 &adjusted_input_text); | 87 &adjusted_input_text); |
88 | 88 |
89 const string16 text = adjusted_input_text + suggest_text; | 89 const string16 text = adjusted_input_text + suggest_text; |
90 // Remove any matches that are identical to |text|. We don't use the | 90 // Remove any matches that are identical to |text|. We don't use the |
91 // destination_url for comparison as it varies depending upon the index passed | 91 // destination_url for comparison as it varies depending upon the index passed |
92 // to TemplateURL::ReplaceSearchTerms. | 92 // to TemplateURL::ReplaceSearchTerms. |
93 for (ACMatches::iterator i = matches_.begin(); i != matches_.end();) { | 93 for (ACMatches::iterator i = matches_.begin(); i != matches_.end();) { |
94 // Reset the description/description_class of all searches. We'll set the | |
95 // description of the new first match in the call to | |
96 // UpdateFirstSearchMatchDescription() below. | |
97 if ((i->type == AutocompleteMatch::SEARCH_HISTORY) || | |
98 (i->type == AutocompleteMatch::SEARCH_SUGGEST) || | |
99 (i->type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED)) { | |
100 i->description.clear(); | |
101 i->description_class.clear(); | |
102 } | |
103 | |
104 if (((i->type == AutocompleteMatch::SEARCH_HISTORY) || | 94 if (((i->type == AutocompleteMatch::SEARCH_HISTORY) || |
105 (i->type == AutocompleteMatch::SEARCH_SUGGEST)) && | 95 (i->type == AutocompleteMatch::SEARCH_SUGGEST)) && |
106 (i->fill_into_edit == text)) { | 96 (i->fill_into_edit == text)) { |
107 i = matches_.erase(i); | 97 i = matches_.erase(i); |
108 } else { | 98 } else { |
109 ++i; | 99 ++i; |
110 } | 100 } |
111 } | 101 } |
112 | 102 |
113 // Add the new suggest result. We give it a rank higher than | 103 // Add the new suggest result. We give it a rank higher than |
114 // SEARCH_WHAT_YOU_TYPED so that it gets autocompleted. | 104 // SEARCH_WHAT_YOU_TYPED so that it gets autocompleted. |
115 int did_not_accept_default_suggestion = default_suggest_results_.empty() ? | 105 int did_not_accept_default_suggestion = default_suggest_results_.empty() ? |
116 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : | 106 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : |
117 TemplateURLRef::NO_SUGGESTION_CHOSEN; | 107 TemplateURLRef::NO_SUGGESTION_CHOSEN; |
118 MatchMap match_map; | 108 MatchMap match_map; |
119 AddMatchToMap(text, adjusted_input_text, | 109 AddMatchToMap(text, adjusted_input_text, |
120 CalculateRelevanceForWhatYouTyped() + 1, | 110 CalculateRelevanceForWhatYouTyped() + 1, |
121 AutocompleteMatch::SEARCH_SUGGEST, | 111 AutocompleteMatch::SEARCH_SUGGEST, |
122 did_not_accept_default_suggestion, false, | 112 did_not_accept_default_suggestion, false, |
123 input_.prevent_inline_autocomplete(), &match_map); | 113 input_.prevent_inline_autocomplete(), &match_map); |
124 DCHECK_EQ(1u, match_map.size()); | 114 DCHECK_EQ(1u, match_map.size()); |
125 matches_.push_back(match_map.begin()->second); | 115 matches_.push_back(match_map.begin()->second); |
126 // Sort the results so that UpdateFirstSearchDescription does the right thing. | |
127 std::sort(matches_.begin(), matches_.end(), &AutocompleteMatch::MoreRelevant); | |
128 | |
129 UpdateFirstSearchMatchDescription(); | |
130 | 116 |
131 listener_->OnProviderUpdate(true); | 117 listener_->OnProviderUpdate(true); |
132 } | 118 } |
133 | 119 |
134 void SearchProvider::Start(const AutocompleteInput& input, | 120 void SearchProvider::Start(const AutocompleteInput& input, |
135 bool minimal_changes) { | 121 bool minimal_changes) { |
136 matches_.clear(); | 122 matches_.clear(); |
137 | 123 |
138 instant_finalized_ = | 124 instant_finalized_ = |
139 (input.matches_requested() != AutocompleteInput::ALL_MATCHES); | 125 (input.matches_requested() != AutocompleteInput::ALL_MATCHES); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 | 166 |
181 if (input.text().empty()) { | 167 if (input.text().empty()) { |
182 // User typed "?" alone. Give them a placeholder result indicating what | 168 // User typed "?" alone. Give them a placeholder result indicating what |
183 // this syntax does. | 169 // this syntax does. |
184 if (default_provider) { | 170 if (default_provider) { |
185 AutocompleteMatch match; | 171 AutocompleteMatch match; |
186 match.provider = this; | 172 match.provider = this; |
187 match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)); | 173 match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)); |
188 match.contents_class.push_back( | 174 match.contents_class.push_back( |
189 ACMatchClassification(0, ACMatchClassification::NONE)); | 175 ACMatchClassification(0, ACMatchClassification::NONE)); |
| 176 match.template_url = &providers_.default_provider(); |
190 matches_.push_back(match); | 177 matches_.push_back(match); |
191 UpdateFirstSearchMatchDescription(); | |
192 } | 178 } |
193 Stop(); | 179 Stop(); |
194 return; | 180 return; |
195 } | 181 } |
196 | 182 |
197 input_ = input; | 183 input_ = input; |
198 | 184 |
199 DoHistoryQuery(minimal_changes); | 185 DoHistoryQuery(minimal_changes); |
200 StartOrStopSuggestQuery(minimal_changes); | 186 StartOrStopSuggestQuery(minimal_changes); |
201 ConvertResultsToAutocompleteMatches(); | 187 ConvertResultsToAutocompleteMatches(); |
(...skipping 20 matching lines...) Expand all Loading... |
222 // providers. | 208 // providers. |
223 DCHECK_GT(suggest_results_pending_, 0); | 209 DCHECK_GT(suggest_results_pending_, 0); |
224 } | 210 } |
225 | 211 |
226 void SearchProvider::Stop() { | 212 void SearchProvider::Stop() { |
227 StopSuggest(); | 213 StopSuggest(); |
228 done_ = true; | 214 done_ = true; |
229 default_provider_suggest_text_.clear(); | 215 default_provider_suggest_text_.clear(); |
230 } | 216 } |
231 | 217 |
| 218 void SearchProvider::PostProcessResult(AutocompleteResult* result) { |
| 219 // For each group of contiguous matches from the same TemplateURL, show the |
| 220 // provider name as a description on the first match in the group. |
| 221 const TemplateURL* last_template_url = NULL; |
| 222 for (AutocompleteResult::iterator i = result->begin(); i != result->end(); |
| 223 ++i) { |
| 224 if (i->provider == this && |
| 225 (i->type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED || |
| 226 i->type == AutocompleteMatch::SEARCH_HISTORY || |
| 227 i->type == AutocompleteMatch::SEARCH_SUGGEST)) { |
| 228 i->description.clear(); |
| 229 i->description_class.clear(); |
| 230 DCHECK(i->template_url); // We should always set a template_url |
| 231 if (last_template_url != i->template_url) { |
| 232 i->description = l10n_util::GetStringFUTF16( |
| 233 IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION, |
| 234 i->template_url->AdjustedShortNameForLocaleDirection()); |
| 235 i->description_class.push_back( |
| 236 ACMatchClassification(0, ACMatchClassification::DIM)); |
| 237 } |
| 238 last_template_url = i->template_url; |
| 239 } else { |
| 240 last_template_url = NULL; |
| 241 } |
| 242 } |
| 243 } |
| 244 |
232 void SearchProvider::OnURLFetchComplete(const URLFetcher* source, | 245 void SearchProvider::OnURLFetchComplete(const URLFetcher* source, |
233 const GURL& url, | 246 const GURL& url, |
234 const net::URLRequestStatus& status, | 247 const net::URLRequestStatus& status, |
235 int response_code, | 248 int response_code, |
236 const net::ResponseCookies& cookie, | 249 const net::ResponseCookies& cookie, |
237 const std::string& data) { | 250 const std::string& data) { |
238 DCHECK(!done_); | 251 DCHECK(!done_); |
239 suggest_results_pending_--; | 252 suggest_results_pending_--; |
240 DCHECK_GE(suggest_results_pending_, 0); // Should never go negative. | 253 DCHECK_GE(suggest_results_pending_, 0); // Should never go negative. |
241 const net::HttpResponseHeaders* const response_headers = | 254 const net::HttpResponseHeaders* const response_headers = |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
570 AddNavigationResultsToMatches(keyword_navigation_results_, true); | 583 AddNavigationResultsToMatches(keyword_navigation_results_, true); |
571 AddNavigationResultsToMatches(default_navigation_results_, false); | 584 AddNavigationResultsToMatches(default_navigation_results_, false); |
572 | 585 |
573 const size_t max_total_matches = kMaxMatches + 1; // 1 for "what you typed" | 586 const size_t max_total_matches = kMaxMatches + 1; // 1 for "what you typed" |
574 std::partial_sort(matches_.begin(), | 587 std::partial_sort(matches_.begin(), |
575 matches_.begin() + std::min(max_total_matches, matches_.size()), | 588 matches_.begin() + std::min(max_total_matches, matches_.size()), |
576 matches_.end(), &AutocompleteMatch::MoreRelevant); | 589 matches_.end(), &AutocompleteMatch::MoreRelevant); |
577 if (matches_.size() > max_total_matches) | 590 if (matches_.size() > max_total_matches) |
578 matches_.erase(matches_.begin() + max_total_matches, matches_.end()); | 591 matches_.erase(matches_.begin() + max_total_matches, matches_.end()); |
579 | 592 |
580 UpdateFirstSearchMatchDescription(); | |
581 | |
582 UpdateStarredStateOfMatches(); | 593 UpdateStarredStateOfMatches(); |
583 | 594 |
584 UpdateDone(); | 595 UpdateDone(); |
585 } | 596 } |
586 | 597 |
587 void SearchProvider::AddNavigationResultsToMatches( | 598 void SearchProvider::AddNavigationResultsToMatches( |
588 const NavigationResults& navigation_results, | 599 const NavigationResults& navigation_results, |
589 bool is_keyword) { | 600 bool is_keyword) { |
590 if (!navigation_results.empty()) { | 601 if (!navigation_results.empty()) { |
591 // TODO(kochi): http://b/1170574 We add only one results for navigational | 602 // TODO(kochi): http://b/1170574 We add only one results for navigational |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
751 int relevance, | 762 int relevance, |
752 AutocompleteMatch::Type type, | 763 AutocompleteMatch::Type type, |
753 int accepted_suggestion, | 764 int accepted_suggestion, |
754 bool is_keyword, | 765 bool is_keyword, |
755 bool prevent_inline_autocomplete, | 766 bool prevent_inline_autocomplete, |
756 MatchMap* map) { | 767 MatchMap* map) { |
757 AutocompleteMatch match(this, relevance, false, type); | 768 AutocompleteMatch match(this, relevance, false, type); |
758 std::vector<size_t> content_param_offsets; | 769 std::vector<size_t> content_param_offsets; |
759 const TemplateURL& provider = is_keyword ? providers_.keyword_provider() : | 770 const TemplateURL& provider = is_keyword ? providers_.keyword_provider() : |
760 providers_.default_provider(); | 771 providers_.default_provider(); |
| 772 match.template_url = &provider; |
761 match.contents.assign(query_string); | 773 match.contents.assign(query_string); |
762 // We do intra-string highlighting for suggestions - the suggested segment | 774 // We do intra-string highlighting for suggestions - the suggested segment |
763 // will be highlighted, e.g. for input_text = "you" the suggestion may be | 775 // will be highlighted, e.g. for input_text = "you" the suggestion may be |
764 // "youtube", so we'll bold the "tube" section: you*tube*. | 776 // "youtube", so we'll bold the "tube" section: you*tube*. |
765 if (input_text != query_string) { | 777 if (input_text != query_string) { |
766 size_t input_position = match.contents.find(input_text); | 778 size_t input_position = match.contents.find(input_text); |
767 if (input_position == string16::npos) { | 779 if (input_position == string16::npos) { |
768 // The input text is not a substring of the query string, e.g. input | 780 // The input text is not a substring of the query string, e.g. input |
769 // text is "slasdot" and the query string is "slashdot", so we bold the | 781 // text is "slasdot" and the query string is "slashdot", so we bold the |
770 // whole thing. | 782 // whole thing. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 // values preserve that property. Otherwise, if the user starts editing a | 814 // values preserve that property. Otherwise, if the user starts editing a |
803 // suggestion, non-Search results will suddenly appear. | 815 // suggestion, non-Search results will suddenly appear. |
804 size_t search_start = 0; | 816 size_t search_start = 0; |
805 if (input_.type() == AutocompleteInput::FORCED_QUERY) { | 817 if (input_.type() == AutocompleteInput::FORCED_QUERY) { |
806 match.fill_into_edit.assign(ASCIIToUTF16("?")); | 818 match.fill_into_edit.assign(ASCIIToUTF16("?")); |
807 ++search_start; | 819 ++search_start; |
808 } | 820 } |
809 if (is_keyword) { | 821 if (is_keyword) { |
810 match.fill_into_edit.append( | 822 match.fill_into_edit.append( |
811 providers_.keyword_provider().keyword() + char16(' ')); | 823 providers_.keyword_provider().keyword() + char16(' ')); |
812 match.template_url = &providers_.keyword_provider(); | |
813 } | 824 } |
814 match.fill_into_edit.append(query_string); | 825 match.fill_into_edit.append(query_string); |
815 // Not all suggestions start with the original input. | 826 // Not all suggestions start with the original input. |
816 if (!prevent_inline_autocomplete && | 827 if (!prevent_inline_autocomplete && |
817 !match.fill_into_edit.compare(search_start, input_text.length(), | 828 !match.fill_into_edit.compare(search_start, input_text.length(), |
818 input_text)) | 829 input_text)) |
819 match.inline_autocomplete_offset = search_start + input_text.length(); | 830 match.inline_autocomplete_offset = search_start + input_text.length(); |
820 | 831 |
821 const TemplateURLRef* const search_url = provider.url(); | 832 const TemplateURLRef* const search_url = provider.url(); |
822 DCHECK(search_url->SupportsReplacement()); | 833 DCHECK(search_url->SupportsReplacement()); |
(...skipping 29 matching lines...) Expand all Loading... |
852 const NavigationResult& navigation, | 863 const NavigationResult& navigation, |
853 int relevance, | 864 int relevance, |
854 bool is_keyword) { | 865 bool is_keyword) { |
855 const string16& input_text = | 866 const string16& input_text = |
856 is_keyword ? keyword_input_text_ : input_.text(); | 867 is_keyword ? keyword_input_text_ : input_.text(); |
857 AutocompleteMatch match(this, relevance, false, | 868 AutocompleteMatch match(this, relevance, false, |
858 AutocompleteMatch::NAVSUGGEST); | 869 AutocompleteMatch::NAVSUGGEST); |
859 match.destination_url = navigation.url; | 870 match.destination_url = navigation.url; |
860 match.contents = | 871 match.contents = |
861 StringForURLDisplay(navigation.url, true, !HasHTTPScheme(input_text)); | 872 StringForURLDisplay(navigation.url, true, !HasHTTPScheme(input_text)); |
| 873 match.template_url = is_keyword ? &providers_.keyword_provider() : |
| 874 &providers_.default_provider(); |
862 AutocompleteMatch::ClassifyMatchInString(input_text, match.contents, | 875 AutocompleteMatch::ClassifyMatchInString(input_text, match.contents, |
863 ACMatchClassification::URL, | 876 ACMatchClassification::URL, |
864 &match.contents_class); | 877 &match.contents_class); |
865 | 878 |
866 match.description = navigation.site_name; | 879 match.description = navigation.site_name; |
867 AutocompleteMatch::ClassifyMatchInString(input_text, navigation.site_name, | 880 AutocompleteMatch::ClassifyMatchInString(input_text, navigation.site_name, |
868 ACMatchClassification::NONE, | 881 ACMatchClassification::NONE, |
869 &match.description_class); | 882 &match.description_class); |
870 | 883 |
871 // When the user forced a query, we need to make sure all the fill_into_edit | 884 // When the user forced a query, we need to make sure all the fill_into_edit |
872 // values preserve that property. Otherwise, if the user starts editing a | 885 // values preserve that property. Otherwise, if the user starts editing a |
873 // suggestion, non-Search results will suddenly appear. | 886 // suggestion, non-Search results will suddenly appear. |
874 if (input_.type() == AutocompleteInput::FORCED_QUERY) | 887 if (input_.type() == AutocompleteInput::FORCED_QUERY) |
875 match.fill_into_edit.assign(ASCIIToUTF16("?")); | 888 match.fill_into_edit.assign(ASCIIToUTF16("?")); |
876 match.fill_into_edit.append( | 889 match.fill_into_edit.append( |
877 AutocompleteInput::FormattedStringWithEquivalentMeaning(navigation.url, | 890 AutocompleteInput::FormattedStringWithEquivalentMeaning(navigation.url, |
878 match.contents)); | 891 match.contents)); |
879 // TODO(pkasting): http://b/1112879 These should perhaps be | 892 // TODO(pkasting): http://b/1112879 These should perhaps be |
880 // inline-autocompletable? | 893 // inline-autocompletable? |
881 | 894 |
882 return match; | 895 return match; |
883 } | 896 } |
884 | 897 |
885 void SearchProvider::UpdateDone() { | 898 void SearchProvider::UpdateDone() { |
886 // We're done when there are no more suggest queries pending (this is set to 1 | 899 // We're done when there are no more suggest queries pending (this is set to 1 |
887 // when the timer is started) and we're not waiting on instant. | 900 // when the timer is started) and we're not waiting on instant. |
888 done_ = ((suggest_results_pending_ == 0) && | 901 done_ = ((suggest_results_pending_ == 0) && |
889 (instant_finalized_ || !InstantController::IsEnabled(profile_))); | 902 (instant_finalized_ || !InstantController::IsEnabled(profile_))); |
890 } | 903 } |
891 | |
892 void SearchProvider::UpdateFirstSearchMatchDescription() { | |
893 if (!providers_.valid_default_provider() || matches_.empty()) | |
894 return; | |
895 | |
896 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) { | |
897 AutocompleteMatch& match = *i; | |
898 switch (match.type) { | |
899 case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED: | |
900 case AutocompleteMatch::SEARCH_HISTORY: | |
901 case AutocompleteMatch::SEARCH_SUGGEST: | |
902 match.description.assign(l10n_util::GetStringFUTF16( | |
903 IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION, | |
904 providers_.default_provider(). | |
905 AdjustedShortNameForLocaleDirection())); | |
906 match.description_class.push_back( | |
907 ACMatchClassification(0, ACMatchClassification::DIM)); | |
908 // Only the first search match gets a description. | |
909 return; | |
910 | |
911 default: | |
912 break; | |
913 } | |
914 } | |
915 } | |
OLD | NEW |