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 "chrome/browser/autocomplete/zero_suggest_provider.h" | 5 #include "chrome/browser/autocomplete/zero_suggest_provider.h" |
6 | 6 |
7 #include "base/callback.h" | 7 #include "base/callback.h" |
8 #include "base/i18n/case_conversion.h" | 8 #include "base/i18n/case_conversion.h" |
9 #include "base/json/json_string_value_serializer.h" | 9 #include "base/json/json_string_value_serializer.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
11 #include "base/metrics/user_metrics.h" | |
12 #include "base/prefs/pref_service.h" | 11 #include "base/prefs/pref_service.h" |
13 #include "base/strings/string16.h" | 12 #include "base/strings/string16.h" |
14 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
15 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
16 #include "base/time/time.h" | 15 #include "base/time/time.h" |
17 #include "chrome/browser/autocomplete/autocomplete_classifier.h" | 16 #include "chrome/browser/autocomplete/autocomplete_classifier.h" |
18 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" | 17 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" |
19 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h" | 18 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h" |
20 #include "chrome/browser/autocomplete/history_url_provider.h" | 19 #include "chrome/browser/autocomplete/history_url_provider.h" |
21 #include "chrome/browser/autocomplete/search_provider.h" | 20 #include "chrome/browser/autocomplete/search_provider.h" |
(...skipping 28 matching lines...) Expand all Loading... |
50 // (excluding the end-of-list enum value) | 49 // (excluding the end-of-list enum value) |
51 // We do not want values of existing enums to change or else it screws | 50 // We do not want values of existing enums to change or else it screws |
52 // up the statistics. | 51 // up the statistics. |
53 enum ZeroSuggestRequestsHistogramValue { | 52 enum ZeroSuggestRequestsHistogramValue { |
54 ZERO_SUGGEST_REQUEST_SENT = 1, | 53 ZERO_SUGGEST_REQUEST_SENT = 1, |
55 ZERO_SUGGEST_REQUEST_INVALIDATED, | 54 ZERO_SUGGEST_REQUEST_INVALIDATED, |
56 ZERO_SUGGEST_REPLY_RECEIVED, | 55 ZERO_SUGGEST_REPLY_RECEIVED, |
57 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE | 56 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE |
58 }; | 57 }; |
59 | 58 |
| 59 // User metrics action strings. |
| 60 const char kUserMetricsActionDeletionSuccess[] = |
| 61 "Omnibox.ZeroSuggestDelete.Success"; |
| 62 const char kUserMetricsActionDeletionFailure[] = |
| 63 "Omnibox.ZeroSuggestDelete.Failure"; |
| 64 |
60 void LogOmniboxZeroSuggestRequest( | 65 void LogOmniboxZeroSuggestRequest( |
61 ZeroSuggestRequestsHistogramValue request_value) { | 66 ZeroSuggestRequestsHistogramValue request_value) { |
62 UMA_HISTOGRAM_ENUMERATION("Omnibox.ZeroSuggestRequests", request_value, | 67 UMA_HISTOGRAM_ENUMERATION("Omnibox.ZeroSuggestRequests", request_value, |
63 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE); | 68 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE); |
64 } | 69 } |
65 | 70 |
66 // The maximum relevance of the top match from this provider. | 71 // The maximum relevance of the top match from this provider. |
67 const int kDefaultVerbatimZeroSuggestRelevance = 1300; | 72 const int kDefaultVerbatimZeroSuggestRelevance = 1300; |
68 | 73 |
69 // Relevance value to use if it was not set explicitly by the server. | 74 // Relevance value to use if it was not set explicitly by the server. |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 } | 137 } |
133 | 138 |
134 done_ = false; | 139 done_ = false; |
135 // TODO(jered): Consider adding locally-sourced zero-suggestions here too. | 140 // TODO(jered): Consider adding locally-sourced zero-suggestions here too. |
136 // These may be useful on the NTP or more relevant to the user than server | 141 // These may be useful on the NTP or more relevant to the user than server |
137 // suggestions, if based on local browsing history. | 142 // suggestions, if based on local browsing history. |
138 MaybeUseCachedSuggestions(); | 143 MaybeUseCachedSuggestions(); |
139 Run(suggest_url); | 144 Run(suggest_url); |
140 } | 145 } |
141 | 146 |
| 147 void ZeroSuggestProvider::Stop(bool clear_cached_results) { |
| 148 if (fetcher_) |
| 149 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_INVALIDATED); |
| 150 fetcher_.reset(); |
| 151 done_ = true; |
| 152 |
| 153 if (clear_cached_results) { |
| 154 // We do not call Clear() on |results_| to retain |verbatim_relevance| |
| 155 // value in the |results_| object. |verbatim_relevance| is used at the |
| 156 // beginning of the next StartZeroSuggest() call to determine the current |
| 157 // url match relevance. |
| 158 results_.suggest_results.clear(); |
| 159 results_.navigation_results.clear(); |
| 160 current_query_.clear(); |
| 161 } |
| 162 } |
| 163 |
142 void ZeroSuggestProvider::DeleteMatch(const AutocompleteMatch& match) { | 164 void ZeroSuggestProvider::DeleteMatch(const AutocompleteMatch& match) { |
143 if (OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) { | 165 if (OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) { |
144 // Remove the deleted match from the cache, so it is not shown to the user | 166 // Remove the deleted match from the cache, so it is not shown to the user |
145 // again. Since we cannot remove just one result, blow away the cache. | 167 // again. Since we cannot remove just one result, blow away the cache. |
146 profile_->GetPrefs()->SetString(prefs::kZeroSuggestCachedResults, | 168 profile_->GetPrefs()->SetString(prefs::kZeroSuggestCachedResults, |
147 std::string()); | 169 std::string()); |
148 } | 170 } |
149 BaseSearchProvider::DeleteMatch(match); | 171 BaseSearchProvider::DeleteMatch(match); |
150 } | 172 } |
151 | 173 |
| 174 void ZeroSuggestProvider::AddProviderInfo(ProvidersInfo* provider_info) const { |
| 175 BaseSearchProvider::AddProviderInfo(provider_info); |
| 176 if (!results_.suggest_results.empty() || !results_.navigation_results.empty()) |
| 177 provider_info->back().set_times_returned_results_in_session(1); |
| 178 } |
| 179 |
152 void ZeroSuggestProvider::ResetSession() { | 180 void ZeroSuggestProvider::ResetSession() { |
153 // The user has started editing in the omnibox, so leave | 181 // The user has started editing in the omnibox, so leave |
154 // |field_trial_triggered_in_session_| unchanged and set | 182 // |field_trial_triggered_in_session_| unchanged and set |
155 // |field_trial_triggered_| to false since zero suggest is inactive now. | 183 // |field_trial_triggered_| to false since zero suggest is inactive now. |
156 field_trial_triggered_ = false; | 184 field_trial_triggered_ = false; |
157 } | 185 } |
158 | 186 |
159 void ZeroSuggestProvider::ModifyProviderInfo( | |
160 metrics::OmniboxEventProto_ProviderInfo* provider_info) const { | |
161 if (!results_.suggest_results.empty() || !results_.navigation_results.empty()) | |
162 provider_info->set_times_returned_results_in_session(1); | |
163 } | |
164 | |
165 ZeroSuggestProvider::ZeroSuggestProvider( | 187 ZeroSuggestProvider::ZeroSuggestProvider( |
166 AutocompleteProviderListener* listener, | 188 AutocompleteProviderListener* listener, |
167 TemplateURLService* template_url_service, | 189 TemplateURLService* template_url_service, |
168 Profile* profile) | 190 Profile* profile) |
169 : BaseSearchProvider(template_url_service, profile, | 191 : BaseSearchProvider( |
170 AutocompleteProvider::TYPE_ZERO_SUGGEST), | 192 template_url_service, profile, |
| 193 base::UserMetricsAction(kUserMetricsActionDeletionSuccess), |
| 194 base::UserMetricsAction(kUserMetricsActionDeletionFailure), |
| 195 AutocompleteProvider::TYPE_ZERO_SUGGEST), |
171 listener_(listener), | 196 listener_(listener), |
172 results_from_cache_(false), | 197 results_from_cache_(false), |
173 weak_ptr_factory_(this) { | 198 weak_ptr_factory_(this) { |
174 } | 199 } |
175 | 200 |
176 ZeroSuggestProvider::~ZeroSuggestProvider() { | 201 ZeroSuggestProvider::~ZeroSuggestProvider() { |
177 } | 202 } |
178 | 203 |
179 const TemplateURL* ZeroSuggestProvider::GetTemplateURL(bool is_keyword) const { | |
180 // Zero suggest provider should not receive keyword results. | |
181 DCHECK(!is_keyword); | |
182 return template_url_service_->GetDefaultSearchProvider(); | |
183 } | |
184 | |
185 const AutocompleteInput ZeroSuggestProvider::GetInput(bool is_keyword) const { | |
186 return AutocompleteInput( | |
187 base::string16(), base::string16::npos, base::string16(), | |
188 GURL(current_query_), current_page_classification_, true, false, false, | |
189 true, ChromeAutocompleteSchemeClassifier(profile_)); | |
190 } | |
191 | |
192 bool ZeroSuggestProvider::ShouldAppendExtraParams( | |
193 const SearchSuggestionParser::SuggestResult& result) const { | |
194 // We always use the default provider for search, so append the params. | |
195 return true; | |
196 } | |
197 | |
198 void ZeroSuggestProvider::StopSuggest() { | |
199 if (fetcher_) | |
200 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_INVALIDATED); | |
201 fetcher_.reset(); | |
202 } | |
203 | |
204 void ZeroSuggestProvider::ClearAllResults() { | |
205 // We do not call Clear() on |results_| to retain |verbatim_relevance| | |
206 // value in the |results_| object. |verbatim_relevance| is used at the | |
207 // beginning of the next StartZeroSuggest() call to determine the current url | |
208 // match relevance. | |
209 results_.suggest_results.clear(); | |
210 results_.navigation_results.clear(); | |
211 current_query_.clear(); | |
212 } | |
213 | |
214 void ZeroSuggestProvider::RecordDeletionResult(bool success) { | |
215 if (success) { | |
216 base::RecordAction( | |
217 base::UserMetricsAction("Omnibox.ZeroSuggestDelete.Success")); | |
218 } else { | |
219 base::RecordAction( | |
220 base::UserMetricsAction("Omnibox.ZeroSuggestDelete.Failure")); | |
221 } | |
222 } | |
223 | |
224 void ZeroSuggestProvider::OnURLFetchComplete(const net::URLFetcher* source) { | 204 void ZeroSuggestProvider::OnURLFetchComplete(const net::URLFetcher* source) { |
225 DCHECK(!done_); | 205 DCHECK(!done_); |
226 DCHECK_EQ(fetcher_.get(), source); | 206 DCHECK_EQ(fetcher_.get(), source); |
227 | 207 |
228 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REPLY_RECEIVED); | 208 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REPLY_RECEIVED); |
229 | 209 |
230 bool results_updated = false; | 210 bool results_updated = false; |
231 if (source->GetStatus().is_success() && source->GetResponseCode() == 200) { | 211 if (source->GetStatus().is_success() && source->GetResponseCode() == 200) { |
232 std::string json_data = SearchSuggestionParser::ExtractJsonData(source); | 212 std::string json_data = SearchSuggestionParser::ExtractJsonData(source); |
233 scoped_ptr<base::Value> data( | 213 scoped_ptr<base::Value> data( |
234 SearchSuggestionParser::DeserializeJsonData(json_data)); | 214 SearchSuggestionParser::DeserializeJsonData(json_data)); |
235 if (data) { | 215 if (data) { |
236 if (StoreSuggestionResponse(json_data, *data)) | 216 if (StoreSuggestionResponse(json_data, *data)) |
237 return; | 217 return; |
238 results_updated = ParseSuggestResults( | 218 results_updated = ParseSuggestResults( |
239 *data, kDefaultZeroSuggestRelevance, false, &results_); | 219 *data, GetInput(), kDefaultZeroSuggestRelevance, false, &results_); |
240 } | 220 } |
241 } | 221 } |
242 fetcher_.reset(); | 222 fetcher_.reset(); |
243 done_ = true; | 223 done_ = true; |
244 ConvertResultsToAutocompleteMatches(); | 224 ConvertResultsToAutocompleteMatches(); |
245 listener_->OnProviderUpdate(results_updated); | 225 listener_->OnProviderUpdate(results_updated); |
246 } | 226 } |
247 | 227 |
| 228 AutocompleteInput ZeroSuggestProvider::GetInput() const { |
| 229 return AutocompleteInput( |
| 230 base::string16(), base::string16::npos, base::string16(), |
| 231 GURL(current_query_), current_page_classification_, true, false, false, |
| 232 true, ChromeAutocompleteSchemeClassifier(profile_)); |
| 233 } |
| 234 |
248 bool ZeroSuggestProvider::StoreSuggestionResponse( | 235 bool ZeroSuggestProvider::StoreSuggestionResponse( |
249 const std::string& json_data, | 236 const std::string& json_data, |
250 const base::Value& parsed_data) { | 237 const base::Value& parsed_data) { |
251 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() || | 238 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() || |
252 json_data.empty()) | 239 json_data.empty()) |
253 return false; | 240 return false; |
254 profile_->GetPrefs()->SetString(prefs::kZeroSuggestCachedResults, json_data); | 241 profile_->GetPrefs()->SetString(prefs::kZeroSuggestCachedResults, json_data); |
255 | 242 |
256 // If we received an empty result list, we should update the display, as it | 243 // If we received an empty result list, we should update the display, as it |
257 // may be showing cached results that should not be shown. | 244 // may be showing cached results that should not be shown. |
258 const base::ListValue* root_list = NULL; | 245 const base::ListValue* root_list = NULL; |
259 const base::ListValue* results_list = NULL; | 246 const base::ListValue* results_list = NULL; |
260 if (parsed_data.GetAsList(&root_list) && | 247 if (parsed_data.GetAsList(&root_list) && |
261 root_list->GetList(1, &results_list) && | 248 root_list->GetList(1, &results_list) && |
262 results_list->empty()) | 249 results_list->empty()) |
263 return false; | 250 return false; |
264 | 251 |
265 // We are finished with the request and want to bail early. | 252 // We are finished with the request and want to bail early. |
266 if (results_from_cache_) | 253 if (results_from_cache_) |
267 done_ = true; | 254 done_ = true; |
268 | 255 |
269 return results_from_cache_; | 256 return results_from_cache_; |
270 } | 257 } |
271 | 258 |
272 void ZeroSuggestProvider::AddSuggestResultsToMap( | 259 void ZeroSuggestProvider::AddSuggestResultsToMap( |
273 const SearchSuggestionParser::SuggestResults& results, | 260 const SearchSuggestionParser::SuggestResults& results, |
274 MatchMap* map) { | 261 MatchMap* map) { |
275 for (size_t i = 0; i < results.size(); ++i) | 262 for (size_t i = 0; i < results.size(); ++i) { |
276 AddMatchToMap(results[i], std::string(), i, false, map); | 263 AddMatchToMap(GetInput(), results[i], |
| 264 template_url_service_->GetDefaultSearchProvider(), |
| 265 std::string(), i, true, false, map); |
| 266 } |
277 } | 267 } |
278 | 268 |
279 AutocompleteMatch ZeroSuggestProvider::NavigationToMatch( | 269 AutocompleteMatch ZeroSuggestProvider::NavigationToMatch( |
280 const SearchSuggestionParser::NavigationResult& navigation) { | 270 const SearchSuggestionParser::NavigationResult& navigation) { |
281 AutocompleteMatch match(this, navigation.relevance(), false, | 271 AutocompleteMatch match(this, navigation.relevance(), false, |
282 navigation.type()); | 272 navigation.type()); |
283 match.destination_url = navigation.url(); | 273 match.destination_url = navigation.url(); |
284 | 274 |
285 // Zero suggest results should always omit protocols and never appear bold. | 275 // Zero suggest results should always omit protocols and never appear bold. |
286 const std::string languages( | 276 const std::string languages( |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
448 | 438 |
449 void ZeroSuggestProvider::MaybeUseCachedSuggestions() { | 439 void ZeroSuggestProvider::MaybeUseCachedSuggestions() { |
450 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) | 440 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) |
451 return; | 441 return; |
452 | 442 |
453 std::string json_data = profile_->GetPrefs()->GetString( | 443 std::string json_data = profile_->GetPrefs()->GetString( |
454 prefs::kZeroSuggestCachedResults); | 444 prefs::kZeroSuggestCachedResults); |
455 if (!json_data.empty()) { | 445 if (!json_data.empty()) { |
456 scoped_ptr<base::Value> data( | 446 scoped_ptr<base::Value> data( |
457 SearchSuggestionParser::DeserializeJsonData(json_data)); | 447 SearchSuggestionParser::DeserializeJsonData(json_data)); |
458 if (data && ParseSuggestResults( | 448 if (data && |
459 *data, kDefaultZeroSuggestRelevance, false, &results_)) { | 449 ParseSuggestResults(*data, GetInput(), kDefaultZeroSuggestRelevance, |
| 450 false, &results_)) { |
460 ConvertResultsToAutocompleteMatches(); | 451 ConvertResultsToAutocompleteMatches(); |
461 results_from_cache_ = !matches_.empty(); | 452 results_from_cache_ = !matches_.empty(); |
462 } | 453 } |
463 } | 454 } |
464 } | 455 } |
OLD | NEW |