zero_suggest_provider.cc revision 010d83a9304c5a91596085d917d248abff47903a
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/zero_suggest_provider.h" 690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/case_conversion.h" 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_string_value_serializer.h" 1090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/metrics/histogram.h" 11d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/prefs/pref_service.h" 1290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/strings/string16.h" 1390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/strings/string_util.h" 14effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/strings/utf_string_conversions.h" 15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/time/time.h" 1690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_classifier.h" 1790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h" 1890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_input.h" 1990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_match.h" 20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "chrome/browser/autocomplete/autocomplete_provider_listener.h" 2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/history_url_provider.h" 2290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/search_provider.h" 2390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/autocomplete/url_prefix.h" 2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/history/history_types.h" 2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/history/top_sites.h" 2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/metrics/variations/variations_http_header_provider.h" 2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/omnibox/omnibox_field_trial.h" 2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/profiles/profile.h" 2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/search/search.h" 3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/search_engines/template_url_service.h" 3190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/search_engines/template_url_service_factory.h" 3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/common/net/url_fixer_upper.h" 3390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/common/pref_names.h" 3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/common/url_constants.h" 3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "components/user_prefs/pref_registry_syncable.h" 36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "content/public/browser/user_metrics.h" 3790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "net/base/escape.h" 3890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "net/base/load_flags.h" 3990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "net/base/net_util.h" 4090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "net/http/http_request_headers.h" 4190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "net/url_request/url_fetcher.h" 4290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "net/url_request/url_request_status.h" 4390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "url/gurl.h" 4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)namespace { 4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 4790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// TODO(hfung): The histogram code was copied and modified from 4890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// search_provider.cc. Refactor and consolidate the code. 4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// We keep track in a histogram how many suggest requests we send, how 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// many suggest requests we invalidate (e.g., due to a user typing 51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// another character), and how many replies we receive. 52effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// *** ADD NEW ENUMS AFTER ALL PREVIOUSLY DEFINED ONES! *** 5390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// (excluding the end-of-list enum value) 5490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// We do not want values of existing enums to change or else it screws 5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// up the statistics. 5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)enum ZeroSuggestRequestsHistogramValue { 5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ZERO_SUGGEST_REQUEST_SENT = 1, 5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ZERO_SUGGEST_REQUEST_INVALIDATED, 5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ZERO_SUGGEST_REPLY_RECEIVED, 6090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE 6190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}; 6290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 6390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void LogOmniboxZeroSuggestRequest( 64 ZeroSuggestRequestsHistogramValue request_value) { 65 UMA_HISTOGRAM_ENUMERATION("Omnibox.ZeroSuggestRequests", request_value, 66 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE); 67} 68 69// The maximum relevance of the top match from this provider. 70const int kDefaultVerbatimZeroSuggestRelevance = 1300; 71 72// Relevance value to use if it was not set explicitly by the server. 73const int kDefaultZeroSuggestRelevance = 100; 74 75} // namespace 76 77// static 78ZeroSuggestProvider* ZeroSuggestProvider::Create( 79 AutocompleteProviderListener* listener, 80 Profile* profile) { 81 return new ZeroSuggestProvider(listener, profile); 82} 83 84// static 85void ZeroSuggestProvider::RegisterProfilePrefs( 86 user_prefs::PrefRegistrySyncable* registry) { 87 registry->RegisterStringPref( 88 prefs::kZeroSuggestCachedResults, 89 std::string(), 90 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 91} 92 93void ZeroSuggestProvider::Start(const AutocompleteInput& input, 94 bool minimal_changes) { 95 matches_.clear(); 96 if (input.type() == AutocompleteInput::INVALID) 97 return; 98 99 Stop(true); 100 field_trial_triggered_ = false; 101 field_trial_triggered_in_session_ = false; 102 results_from_cache_ = false; 103 permanent_text_ = input.text(); 104 current_query_ = input.current_url().spec(); 105 current_page_classification_ = input.current_page_classification(); 106 current_url_match_ = MatchForCurrentURL(); 107 108 const TemplateURL* default_provider = 109 template_url_service_->GetDefaultSearchProvider(); 110 if (default_provider == NULL) 111 return; 112 113 base::string16 prefix; 114 TemplateURLRef::SearchTermsArgs search_term_args(prefix); 115 GURL suggest_url(default_provider->suggestions_url_ref().ReplaceSearchTerms( 116 search_term_args)); 117 if (!suggest_url.is_valid()) 118 return; 119 120 // No need to send the current page URL in personalized suggest field trial. 121 if (CanSendURL(input.current_url(), suggest_url, default_provider, 122 current_page_classification_, profile_) && 123 !OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) { 124 // Update suggest_url to include the current_page_url. 125 search_term_args.current_page_url = current_query_; 126 suggest_url = GURL(default_provider->suggestions_url_ref(). 127 ReplaceSearchTerms(search_term_args)); 128 } else if (!CanShowZeroSuggestWithoutSendingURL(suggest_url, 129 input.current_url())) { 130 return; 131 } 132 133 done_ = false; 134 // TODO(jered): Consider adding locally-sourced zero-suggestions here too. 135 // These may be useful on the NTP or more relevant to the user than server 136 // suggestions, if based on local browsing history. 137 MaybeUseCachedSuggestions(); 138 Run(suggest_url); 139} 140 141void ZeroSuggestProvider::DeleteMatch(const AutocompleteMatch& match) { 142 if (OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) { 143 // Remove the deleted match from the cache, so it is not shown to the user 144 // again. Since we cannot remove just one result, blow away the cache. 145 profile_->GetPrefs()->SetString(prefs::kZeroSuggestCachedResults, 146 std::string()); 147 } 148 BaseSearchProvider::DeleteMatch(match); 149} 150 151void ZeroSuggestProvider::ResetSession() { 152 // The user has started editing in the omnibox, so leave 153 // |field_trial_triggered_in_session_| unchanged and set 154 // |field_trial_triggered_| to false since zero suggest is inactive now. 155 field_trial_triggered_ = false; 156} 157 158void ZeroSuggestProvider::ModifyProviderInfo( 159 metrics::OmniboxEventProto_ProviderInfo* provider_info) const { 160 if (!results_.suggest_results.empty() || !results_.navigation_results.empty()) 161 provider_info->set_times_returned_results_in_session(1); 162} 163 164ZeroSuggestProvider::ZeroSuggestProvider( 165 AutocompleteProviderListener* listener, 166 Profile* profile) 167 : BaseSearchProvider(listener, profile, 168 AutocompleteProvider::TYPE_ZERO_SUGGEST), 169 template_url_service_(TemplateURLServiceFactory::GetForProfile(profile)), 170 results_from_cache_(false), 171 weak_ptr_factory_(this) { 172} 173 174ZeroSuggestProvider::~ZeroSuggestProvider() { 175} 176 177bool ZeroSuggestProvider::StoreSuggestionResponse( 178 const std::string& json_data, 179 const base::Value& parsed_data) { 180 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() || 181 json_data.empty()) 182 return false; 183 profile_->GetPrefs()->SetString(prefs::kZeroSuggestCachedResults, json_data); 184 185 // If we received an empty result list, we should update the display, as it 186 // may be showing cached results that should not be shown. 187 const base::ListValue* root_list = NULL; 188 const base::ListValue* results_list = NULL; 189 if (parsed_data.GetAsList(&root_list) && 190 root_list->GetList(1, &results_list) && 191 results_list->empty()) 192 return false; 193 194 // We are finished with the request and want to bail early. 195 if (results_from_cache_) 196 done_ = true; 197 198 return results_from_cache_; 199} 200 201const TemplateURL* ZeroSuggestProvider::GetTemplateURL(bool is_keyword) const { 202 // Zero suggest provider should not receive keyword results. 203 DCHECK(!is_keyword); 204 return template_url_service_->GetDefaultSearchProvider(); 205} 206 207const AutocompleteInput ZeroSuggestProvider::GetInput(bool is_keyword) const { 208 return AutocompleteInput( 209 base::string16(), base::string16::npos, base::string16(), 210 GURL(current_query_), current_page_classification_, true, false, false, 211 true); 212} 213 214BaseSearchProvider::Results* ZeroSuggestProvider::GetResultsToFill( 215 bool is_keyword) { 216 DCHECK(!is_keyword); 217 return &results_; 218} 219 220bool ZeroSuggestProvider::ShouldAppendExtraParams( 221 const SuggestResult& result) const { 222 // We always use the default provider for search, so append the params. 223 return true; 224} 225 226void ZeroSuggestProvider::StopSuggest() { 227 if (suggest_results_pending_ > 0) 228 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_INVALIDATED); 229 suggest_results_pending_ = 0; 230 fetcher_.reset(); 231} 232 233void ZeroSuggestProvider::ClearAllResults() { 234 // We do not call Clear() on |results_| to retain |verbatim_relevance| 235 // value in the |results_| object. |verbatim_relevance| is used at the 236 // beginning of the next StartZeroSuggest() call to determine the current url 237 // match relevance. 238 results_.suggest_results.clear(); 239 results_.navigation_results.clear(); 240 current_query_.clear(); 241} 242 243int ZeroSuggestProvider::GetDefaultResultRelevance() const { 244 return kDefaultZeroSuggestRelevance; 245} 246 247void ZeroSuggestProvider::RecordDeletionResult(bool success) { 248 if (success) { 249 content::RecordAction( 250 base::UserMetricsAction("Omnibox.ZeroSuggestDelete.Success")); 251 } else { 252 content::RecordAction( 253 base::UserMetricsAction("Omnibox.ZeroSuggestDelete.Failure")); 254 } 255} 256 257void ZeroSuggestProvider::LogFetchComplete(bool success, bool is_keyword) { 258 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REPLY_RECEIVED); 259} 260 261bool ZeroSuggestProvider::IsKeywordFetcher( 262 const net::URLFetcher* fetcher) const { 263 // ZeroSuggestProvider does not have a keyword provider. 264 DCHECK_EQ(fetcher, fetcher_.get()); 265 return false; 266} 267 268void ZeroSuggestProvider::UpdateMatches() { 269 done_ = true; 270 ConvertResultsToAutocompleteMatches(); 271} 272 273void ZeroSuggestProvider::AddSuggestResultsToMap( 274 const SuggestResults& results, 275 MatchMap* map) { 276 for (size_t i = 0; i < results.size(); ++i) 277 AddMatchToMap(results[i], std::string(), i, false, map); 278} 279 280AutocompleteMatch ZeroSuggestProvider::NavigationToMatch( 281 const NavigationResult& navigation) { 282 AutocompleteMatch match(this, navigation.relevance(), false, 283 navigation.type()); 284 match.destination_url = navigation.url(); 285 286 // Zero suggest results should always omit protocols and never appear bold. 287 const std::string languages( 288 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); 289 match.contents = net::FormatUrl(navigation.url(), languages, 290 net::kFormatUrlOmitAll, net::UnescapeRule::SPACES, NULL, NULL, NULL); 291 match.fill_into_edit += 292 AutocompleteInput::FormattedStringWithEquivalentMeaning(navigation.url(), 293 match.contents); 294 295 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0, 296 match.contents.length(), ACMatchClassification::URL, 297 &match.contents_class); 298 299 match.description = 300 AutocompleteMatch::SanitizeString(navigation.description()); 301 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0, 302 match.description.length(), ACMatchClassification::NONE, 303 &match.description_class); 304 return match; 305} 306 307void ZeroSuggestProvider::Run(const GURL& suggest_url) { 308 suggest_results_pending_ = 0; 309 const int kFetcherID = 1; 310 fetcher_.reset( 311 net::URLFetcher::Create(kFetcherID, 312 suggest_url, 313 net::URLFetcher::GET, this)); 314 fetcher_->SetRequestContext(profile_->GetRequestContext()); 315 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); 316 // Add Chrome experiment state to the request headers. 317 net::HttpRequestHeaders headers; 318 chrome_variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( 319 fetcher_->GetOriginalURL(), profile_->IsOffTheRecord(), false, &headers); 320 fetcher_->SetExtraRequestHeaders(headers.ToString()); 321 fetcher_->Start(); 322 323 if (OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()) { 324 most_visited_urls_.clear(); 325 history::TopSites* ts = profile_->GetTopSites(); 326 if (ts) { 327 ts->GetMostVisitedURLs( 328 base::Bind(&ZeroSuggestProvider::OnMostVisitedUrlsAvailable, 329 weak_ptr_factory_.GetWeakPtr()), false); 330 } 331 } 332 suggest_results_pending_ = 1; 333 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT); 334} 335 336void ZeroSuggestProvider::OnMostVisitedUrlsAvailable( 337 const history::MostVisitedURLList& urls) { 338 most_visited_urls_ = urls; 339} 340 341void ZeroSuggestProvider::ConvertResultsToAutocompleteMatches() { 342 matches_.clear(); 343 344 const TemplateURL* default_provider = 345 template_url_service_->GetDefaultSearchProvider(); 346 // Fail if we can't set the clickthrough URL for query suggestions. 347 if (default_provider == NULL || !default_provider->SupportsReplacement()) 348 return; 349 350 MatchMap map; 351 AddSuggestResultsToMap(results_.suggest_results, &map); 352 353 const int num_query_results = map.size(); 354 const int num_nav_results = results_.navigation_results.size(); 355 const int num_results = num_query_results + num_nav_results; 356 UMA_HISTOGRAM_COUNTS("ZeroSuggest.QueryResults", num_query_results); 357 UMA_HISTOGRAM_COUNTS("ZeroSuggest.URLResults", num_nav_results); 358 UMA_HISTOGRAM_COUNTS("ZeroSuggest.AllResults", num_results); 359 360 // Show Most Visited results after ZeroSuggest response is received. 361 if (OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()) { 362 if (!current_url_match_.destination_url.is_valid()) 363 return; 364 matches_.push_back(current_url_match_); 365 int relevance = 600; 366 if (num_results > 0) { 367 UMA_HISTOGRAM_COUNTS( 368 "Omnibox.ZeroSuggest.MostVisitedResultsCounterfactual", 369 most_visited_urls_.size()); 370 } 371 const base::string16 current_query_string16( 372 base::ASCIIToUTF16(current_query_)); 373 const std::string languages( 374 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); 375 for (size_t i = 0; i < most_visited_urls_.size(); i++) { 376 const history::MostVisitedURL& url = most_visited_urls_[i]; 377 NavigationResult nav(*this, url.url, AutocompleteMatchType::NAVSUGGEST, 378 url.title, std::string(), false, relevance, true, 379 current_query_string16, languages); 380 matches_.push_back(NavigationToMatch(nav)); 381 --relevance; 382 } 383 return; 384 } 385 386 if (num_results == 0) 387 return; 388 389 // TODO(jered): Rip this out once the first match is decoupled from the 390 // current typing in the omnibox. 391 matches_.push_back(current_url_match_); 392 393 for (MatchMap::const_iterator it(map.begin()); it != map.end(); ++it) 394 matches_.push_back(it->second); 395 396 const NavigationResults& nav_results(results_.navigation_results); 397 for (NavigationResults::const_iterator it(nav_results.begin()); 398 it != nav_results.end(); ++it) 399 matches_.push_back(NavigationToMatch(*it)); 400} 401 402AutocompleteMatch ZeroSuggestProvider::MatchForCurrentURL() { 403 AutocompleteMatch match; 404 AutocompleteClassifierFactory::GetForProfile(profile_)->Classify( 405 permanent_text_, false, true, current_page_classification_, &match, NULL); 406 match.is_history_what_you_typed_match = false; 407 match.allowed_to_be_default_match = true; 408 409 // The placeholder suggestion for the current URL has high relevance so 410 // that it is in the first suggestion slot and inline autocompleted. It 411 // gets dropped as soon as the user types something. 412 match.relevance = GetVerbatimRelevance(); 413 414 return match; 415} 416 417int ZeroSuggestProvider::GetVerbatimRelevance() const { 418 return results_.verbatim_relevance >= 0 ? 419 results_.verbatim_relevance : kDefaultVerbatimZeroSuggestRelevance; 420} 421 422bool ZeroSuggestProvider::CanShowZeroSuggestWithoutSendingURL( 423 const GURL& suggest_url, 424 const GURL& current_page_url) const { 425 if (!ZeroSuggestEnabled(suggest_url, 426 template_url_service_->GetDefaultSearchProvider(), 427 current_page_classification_, profile_)) 428 return false; 429 430 // If we cannot send URLs, then only the MostVisited and Personalized 431 // variations can be shown. 432 if (!OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial() && 433 !OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) 434 return false; 435 436 // Only show zero suggest for HTTP[S] pages. 437 // TODO(mariakhomenko): We may be able to expand this set to include pages 438 // with other schemes (e.g. chrome://). That may require improvements to 439 // the formatting of the verbatim result returned by MatchForCurrentURL(). 440 if (!current_page_url.is_valid() || 441 ((current_page_url.scheme() != url::kHttpScheme) && 442 (current_page_url.scheme() != url::kHttpsScheme))) 443 return false; 444 445 return true; 446} 447 448void ZeroSuggestProvider::MaybeUseCachedSuggestions() { 449 if (!OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) 450 return; 451 452 std::string json_data = profile_->GetPrefs()->GetString( 453 prefs::kZeroSuggestCachedResults); 454 if (!json_data.empty()) { 455 scoped_ptr<base::Value> data(DeserializeJsonData(json_data)); 456 if (data && ParseSuggestResults(*data.get(), false, &results_)) { 457 ConvertResultsToAutocompleteMatches(); 458 results_from_cache_ = !matches_.empty(); 459 } 460 } 461} 462