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" |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
176 done_ = false; | 176 done_ = false; |
177 // TODO(jered): Consider adding locally-sourced zero-suggestions here too. | 177 // TODO(jered): Consider adding locally-sourced zero-suggestions here too. |
178 // These may be useful on the NTP or more relevant to the user than server | 178 // These may be useful on the NTP or more relevant to the user than server |
179 // suggestions, if based on local browsing history. | 179 // suggestions, if based on local browsing history. |
180 Run(suggest_url); | 180 Run(suggest_url); |
181 } | 181 } |
182 | 182 |
183 ZeroSuggestProvider::ZeroSuggestProvider( | 183 ZeroSuggestProvider::ZeroSuggestProvider( |
184 AutocompleteProviderListener* listener, | 184 AutocompleteProviderListener* listener, |
185 Profile* profile) | 185 Profile* profile) |
186 : AutocompleteProvider(listener, profile, | 186 : BaseSearchProvider(listener, profile, |
187 AutocompleteProvider::TYPE_ZERO_SUGGEST), | 187 AutocompleteProvider::TYPE_ZERO_SUGGEST), |
188 template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)), | 188 template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)), |
189 have_pending_request_(false), | 189 have_pending_request_(false), |
190 verbatim_relevance_(kDefaultVerbatimZeroSuggestRelevance), | 190 verbatim_relevance_(kDefaultVerbatimZeroSuggestRelevance), |
191 field_trial_triggered_(false), | 191 field_trial_triggered_(false), |
192 field_trial_triggered_in_session_(false), | 192 field_trial_triggered_in_session_(false), |
193 weak_ptr_factory_(this) { | 193 weak_ptr_factory_(this) { |
194 } | 194 } |
195 | 195 |
196 ZeroSuggestProvider::~ZeroSuggestProvider() { | 196 ZeroSuggestProvider::~ZeroSuggestProvider() { |
197 } | 197 } |
198 | 198 |
199 void ZeroSuggestProvider::FillResults( | 199 void ZeroSuggestProvider::FillResults(const base::Value& root_val, |
200 const base::Value& root_val, | 200 int* verbatim_relevance, |
201 int* verbatim_relevance, | 201 SuggestResults* suggest_results, |
202 SearchProvider::SuggestResults* suggest_results, | 202 NavigationResults* navigation_results) { |
203 SearchProvider::NavigationResults* navigation_results) { | |
204 base::string16 query; | 203 base::string16 query; |
205 const base::ListValue* root_list = NULL; | 204 const base::ListValue* root_list = NULL; |
206 const base::ListValue* results = NULL; | 205 const base::ListValue* results = NULL; |
207 const base::ListValue* relevances = NULL; | 206 const base::ListValue* relevances = NULL; |
208 // The response includes the query, which should be empty for ZeroSuggest | 207 // The response includes the query, which should be empty for ZeroSuggest |
209 // responses. | 208 // responses. |
210 if (!root_val.GetAsList(&root_list) || !root_list->GetString(0, &query) || | 209 if (!root_val.GetAsList(&root_list) || !root_list->GetString(0, &query) || |
211 (!query.empty()) || !root_list->GetList(1, &results)) | 210 (!query.empty()) || !root_list->GetList(1, &results)) |
212 return; | 211 return; |
213 | 212 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 // Apply valid suggested relevance scores; discard invalid lists. | 259 // Apply valid suggested relevance scores; discard invalid lists. |
261 if (relevances != NULL && !relevances->GetInteger(index, &relevance)) | 260 if (relevances != NULL && !relevances->GetInteger(index, &relevance)) |
262 relevances = NULL; | 261 relevances = NULL; |
263 if (types && types->GetString(index, &type) && (type == "NAVIGATION")) { | 262 if (types && types->GetString(index, &type) && (type == "NAVIGATION")) { |
264 // Do not blindly trust the URL coming from the server to be valid. | 263 // Do not blindly trust the URL coming from the server to be valid. |
265 GURL url(URLFixerUpper::FixupURL( | 264 GURL url(URLFixerUpper::FixupURL( |
266 base::UTF16ToUTF8(result), std::string())); | 265 base::UTF16ToUTF8(result), std::string())); |
267 if (url.is_valid()) { | 266 if (url.is_valid()) { |
268 if (descriptions != NULL) | 267 if (descriptions != NULL) |
269 descriptions->GetString(index, &title); | 268 descriptions->GetString(index, &title); |
270 navigation_results->push_back(SearchProvider::NavigationResult( | 269 navigation_results->push_back(NavigationResult( |
271 *this, url, title, false, relevance, relevances != NULL, | 270 *this, url, title, false, relevance, relevances != NULL, |
272 current_query_string16, languages)); | 271 current_query_string16, languages)); |
273 } | 272 } |
274 } else { | 273 } else { |
275 suggest_results->push_back(SearchProvider::SuggestResult( | 274 suggest_results->push_back(SuggestResult( |
276 result, AutocompleteMatchType::SEARCH_SUGGEST, result, | 275 result, AutocompleteMatchType::SEARCH_SUGGEST, result, |
277 base::string16(), std::string(), std::string(), false, relevance, | 276 base::string16(), std::string(), std::string(), false, relevance, |
278 relevances != NULL, false, current_query_string16)); | 277 relevances != NULL, false, current_query_string16)); |
279 } | 278 } |
280 } | 279 } |
281 } | 280 } |
282 | 281 |
283 void ZeroSuggestProvider::AddSuggestResultsToMap( | 282 void ZeroSuggestProvider::AddSuggestResultsToMap( |
284 const SearchProvider::SuggestResults& results, | 283 const SuggestResults& results, |
285 const TemplateURL* template_url, | 284 const TemplateURL* template_url, |
286 SearchProvider::MatchMap* map) { | 285 MatchMap* map) { |
287 for (size_t i = 0; i < results.size(); ++i) { | 286 for (size_t i = 0; i < results.size(); ++i) { |
288 AddMatchToMap(results[i].relevance(), AutocompleteMatchType::SEARCH_SUGGEST, | 287 AddMatchToMap(results[i].relevance(), AutocompleteMatchType::SEARCH_SUGGEST, |
289 template_url, results[i].suggestion(), i, map); | 288 template_url, results[i].suggestion(), i, map); |
290 } | 289 } |
291 } | 290 } |
292 | 291 |
293 void ZeroSuggestProvider::AddMatchToMap(int relevance, | 292 void ZeroSuggestProvider::AddMatchToMap(int relevance, |
294 AutocompleteMatch::Type type, | 293 AutocompleteMatch::Type type, |
295 const TemplateURL* template_url, | 294 const TemplateURL* template_url, |
296 const base::string16& query_string, | 295 const base::string16& query_string, |
297 int accepted_suggestion, | 296 int accepted_suggestion, |
298 SearchProvider::MatchMap* map) { | 297 MatchMap* map) { |
299 // Pass in query_string as the input_text to avoid bolding. | 298 // Pass in query_string as the input_text to avoid bolding. |
300 SearchProvider::SuggestResult suggestion( | 299 SuggestResult suggestion( |
301 query_string, type, query_string, base::string16(), std::string(), | 300 query_string, type, query_string, base::string16(), std::string(), |
302 std::string(), false, relevance, true, false, query_string); | 301 std::string(), false, relevance, true, false, query_string); |
303 // TODO(samarth|melevin): use the actual omnibox margin here as well instead | 302 // TODO(samarth|melevin): use the actual omnibox margin here as well instead |
304 // of passing in -1. | 303 // of passing in -1. |
305 AutocompleteMatch match = SearchProvider::CreateSearchSuggestion( | 304 AutocompleteMatch match = SearchProvider::CreateSearchSuggestion( |
306 this, AutocompleteInput(), query_string, suggestion, template_url, | 305 this, AutocompleteInput(), query_string, suggestion, template_url, |
307 accepted_suggestion, -1, true); | 306 accepted_suggestion, -1, true); |
308 if (!match.destination_url.is_valid()) | 307 if (!match.destination_url.is_valid()) |
309 return; | 308 return; |
310 | 309 |
311 // Try to add |match| to |map|. If a match for |query_string| is already in | 310 // Try to add |match| to |map|. If a match for |query_string| is already in |
312 // |map|, replace it if |match| is more relevant. | 311 // |map|, replace it if |match| is more relevant. |
313 // NOTE: Keep this ToLower() call in sync with url_database.cc. | 312 // NOTE: Keep this ToLower() call in sync with url_database.cc. |
314 SearchProvider::MatchKey match_key( | 313 MatchKey match_key( |
315 std::make_pair(base::i18n::ToLower(query_string), std::string())); | 314 std::make_pair(base::i18n::ToLower(query_string), std::string())); |
316 const std::pair<SearchProvider::MatchMap::iterator, bool> i(map->insert( | 315 const std::pair<MatchMap::iterator, bool> i(map->insert( |
317 std::make_pair(match_key, match))); | 316 std::make_pair(match_key, match))); |
318 // NOTE: We purposefully do a direct relevance comparison here instead of | 317 // NOTE: We purposefully do a direct relevance comparison here instead of |
319 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added | 318 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added |
320 // first" rather than "items alphabetically first" when the scores are equal. | 319 // first" rather than "items alphabetically first" when the scores are equal. |
321 // The only case this matters is when a user has results with the same score | 320 // The only case this matters is when a user has results with the same score |
322 // that differ only by capitalization; because the history system returns | 321 // that differ only by capitalization; because the history system returns |
323 // results sorted by recency, this means we'll pick the most recent such | 322 // results sorted by recency, this means we'll pick the most recent such |
324 // result even if the precision of our relevance score is too low to | 323 // result even if the precision of our relevance score is too low to |
325 // distinguish the two. | 324 // distinguish the two. |
326 if (!i.second && (match.relevance > i.first->second.relevance)) | 325 if (!i.second && (match.relevance > i.first->second.relevance)) |
327 i.first->second = match; | 326 i.first->second = match; |
328 } | 327 } |
329 | 328 |
330 AutocompleteMatch ZeroSuggestProvider::NavigationToMatch( | 329 AutocompleteMatch ZeroSuggestProvider::NavigationToMatch( |
331 const SearchProvider::NavigationResult& navigation) { | 330 const NavigationResult& navigation) { |
332 AutocompleteMatch match(this, navigation.relevance(), false, | 331 AutocompleteMatch match(this, navigation.relevance(), false, |
333 AutocompleteMatchType::NAVSUGGEST); | 332 AutocompleteMatchType::NAVSUGGEST); |
334 match.destination_url = navigation.url(); | 333 match.destination_url = navigation.url(); |
335 | 334 |
336 // Zero suggest results should always omit protocols and never appear bold. | 335 // Zero suggest results should always omit protocols and never appear bold. |
337 const std::string languages( | 336 const std::string languages( |
338 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); | 337 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); |
339 match.contents = net::FormatUrl(navigation.url(), languages, | 338 match.contents = net::FormatUrl(navigation.url(), languages, |
340 net::kFormatUrlOmitAll, net::UnescapeRule::SPACES, NULL, NULL, NULL); | 339 net::kFormatUrlOmitAll, net::UnescapeRule::SPACES, NULL, NULL, NULL); |
341 match.fill_into_edit += | 340 match.fill_into_edit += |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
378 ts->GetMostVisitedURLs( | 377 ts->GetMostVisitedURLs( |
379 base::Bind(&ZeroSuggestProvider::OnMostVisitedUrlsAvailable, | 378 base::Bind(&ZeroSuggestProvider::OnMostVisitedUrlsAvailable, |
380 weak_ptr_factory_.GetWeakPtr()), false); | 379 weak_ptr_factory_.GetWeakPtr()), false); |
381 } | 380 } |
382 } | 381 } |
383 have_pending_request_ = true; | 382 have_pending_request_ = true; |
384 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT); | 383 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT); |
385 } | 384 } |
386 | 385 |
387 void ZeroSuggestProvider::ParseSuggestResults(const base::Value& root_val) { | 386 void ZeroSuggestProvider::ParseSuggestResults(const base::Value& root_val) { |
388 SearchProvider::SuggestResults suggest_results; | 387 SuggestResults suggest_results; |
389 FillResults(root_val, &verbatim_relevance_, | 388 FillResults(root_val, &verbatim_relevance_, |
390 &suggest_results, &navigation_results_); | 389 &suggest_results, &navigation_results_); |
391 | 390 |
392 query_matches_map_.clear(); | 391 query_matches_map_.clear(); |
393 AddSuggestResultsToMap(suggest_results, | 392 AddSuggestResultsToMap(suggest_results, |
394 template_url_service_->GetDefaultSearchProvider(), | 393 template_url_service_->GetDefaultSearchProvider(), |
395 &query_matches_map_); | 394 &query_matches_map_); |
396 } | 395 } |
397 | 396 |
398 void ZeroSuggestProvider::OnMostVisitedUrlsAvailable( | 397 void ZeroSuggestProvider::OnMostVisitedUrlsAvailable( |
(...skipping 27 matching lines...) Expand all Loading... |
426 UMA_HISTOGRAM_COUNTS( | 425 UMA_HISTOGRAM_COUNTS( |
427 "Omnibox.ZeroSuggest.MostVisitedResultsCounterfactual", | 426 "Omnibox.ZeroSuggest.MostVisitedResultsCounterfactual", |
428 most_visited_urls_.size()); | 427 most_visited_urls_.size()); |
429 } | 428 } |
430 const base::string16 current_query_string16( | 429 const base::string16 current_query_string16( |
431 base::ASCIIToUTF16(current_query_)); | 430 base::ASCIIToUTF16(current_query_)); |
432 const std::string languages( | 431 const std::string languages( |
433 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); | 432 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); |
434 for (size_t i = 0; i < most_visited_urls_.size(); i++) { | 433 for (size_t i = 0; i < most_visited_urls_.size(); i++) { |
435 const history::MostVisitedURL& url = most_visited_urls_[i]; | 434 const history::MostVisitedURL& url = most_visited_urls_[i]; |
436 SearchProvider::NavigationResult nav( | 435 NavigationResult nav(*this, url.url, url.title, false, relevance, true, |
437 *this, url.url, url.title, false, relevance, true, | |
438 current_query_string16, languages); | 436 current_query_string16, languages); |
439 matches_.push_back(NavigationToMatch(nav)); | 437 matches_.push_back(NavigationToMatch(nav)); |
440 --relevance; | 438 --relevance; |
441 } | 439 } |
442 return; | 440 return; |
443 } | 441 } |
444 | 442 |
445 if (num_results == 0) | 443 if (num_results == 0) |
446 return; | 444 return; |
447 | 445 |
448 // TODO(jered): Rip this out once the first match is decoupled from the | 446 // TODO(jered): Rip this out once the first match is decoupled from the |
449 // current typing in the omnibox. | 447 // current typing in the omnibox. |
450 matches_.push_back(current_url_match_); | 448 matches_.push_back(current_url_match_); |
451 | 449 |
452 for (SearchProvider::MatchMap::const_iterator it(query_matches_map_.begin()); | 450 for (MatchMap::const_iterator it(query_matches_map_.begin()); |
453 it != query_matches_map_.end(); ++it) | 451 it != query_matches_map_.end(); ++it) |
454 matches_.push_back(it->second); | 452 matches_.push_back(it->second); |
455 | 453 |
456 for (SearchProvider::NavigationResults::const_iterator it( | 454 for (NavigationResults::const_iterator it(navigation_results_.begin()); |
457 navigation_results_.begin()); it != navigation_results_.end(); ++it) | 455 it != navigation_results_.end(); ++it) |
458 matches_.push_back(NavigationToMatch(*it)); | 456 matches_.push_back(NavigationToMatch(*it)); |
459 } | 457 } |
460 | 458 |
461 AutocompleteMatch ZeroSuggestProvider::MatchForCurrentURL() { | 459 AutocompleteMatch ZeroSuggestProvider::MatchForCurrentURL() { |
462 AutocompleteInput input(permanent_text_, base::string16::npos, base::string16(
), | 460 AutocompleteInput input(permanent_text_, base::string16::npos, base::string16(
), |
463 GURL(current_query_), current_page_classification_, | 461 GURL(current_query_), current_page_classification_, |
464 false, false, true, AutocompleteInput::ALL_MATCHES); | 462 false, false, true, AutocompleteInput::ALL_MATCHES); |
465 | 463 |
466 AutocompleteMatch match; | 464 AutocompleteMatch match; |
467 AutocompleteClassifierFactory::GetForProfile(profile_)->Classify( | 465 AutocompleteClassifierFactory::GetForProfile(profile_)->Classify( |
468 permanent_text_, false, true, &match, NULL); | 466 permanent_text_, false, true, &match, NULL); |
469 match.is_history_what_you_typed_match = false; | 467 match.is_history_what_you_typed_match = false; |
470 match.allowed_to_be_default_match = true; | 468 match.allowed_to_be_default_match = true; |
471 | 469 |
472 // The placeholder suggestion for the current URL has high relevance so | 470 // The placeholder suggestion for the current URL has high relevance so |
473 // that it is in the first suggestion slot and inline autocompleted. It | 471 // that it is in the first suggestion slot and inline autocompleted. It |
474 // gets dropped as soon as the user types something. | 472 // gets dropped as soon as the user types something. |
475 match.relevance = verbatim_relevance_; | 473 match.relevance = verbatim_relevance_; |
476 | 474 |
477 return match; | 475 return match; |
478 } | 476 } |
OLD | NEW |