| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "components/autocomplete/search_suggestion_parser.h" | 5 #include "components/autocomplete/search_suggestion_parser.h" |
| 6 | 6 |
| 7 #include "base/i18n/icu_string_conversions.h" |
| 7 #include "base/json/json_string_value_serializer.h" | 8 #include "base/json/json_string_value_serializer.h" |
| 8 #include "base/json/json_writer.h" | 9 #include "base/json/json_writer.h" |
| 9 #include "base/logging.h" | 10 #include "base/logging.h" |
| 10 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
| 12 #include "base/values.h" | 13 #include "base/values.h" |
| 13 #include "components/autocomplete/autocomplete_input.h" | 14 #include "components/autocomplete/autocomplete_input.h" |
| 14 #include "components/autocomplete/url_prefix.h" | 15 #include "components/autocomplete/url_prefix.h" |
| 15 #include "components/url_fixer/url_fixer.h" | 16 #include "components/url_fixer/url_fixer.h" |
| 16 #include "net/base/net_util.h" | 17 #include "net/base/net_util.h" |
| 18 #include "net/http/http_response_headers.h" |
| 19 #include "net/url_request/url_fetcher.h" |
| 17 | 20 |
| 18 namespace { | 21 namespace { |
| 19 | 22 |
| 20 AutocompleteMatchType::Type GetAutocompleteMatchType(const std::string& type) { | 23 AutocompleteMatchType::Type GetAutocompleteMatchType(const std::string& type) { |
| 21 if (type == "ENTITY") | 24 if (type == "ENTITY") |
| 22 return AutocompleteMatchType::SEARCH_SUGGEST_ENTITY; | 25 return AutocompleteMatchType::SEARCH_SUGGEST_ENTITY; |
| 23 if (type == "INFINITE") | 26 if (type == "INFINITE") |
| 24 return AutocompleteMatchType::SEARCH_SUGGEST_INFINITE; | 27 return AutocompleteMatchType::SEARCH_SUGGEST_INFINITE; |
| 25 if (type == "PERSONALIZED_QUERY") | 28 if (type == "PERSONALIZED_QUERY") |
| 26 return AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED; | 29 return AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED; |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 232 int SearchSuggestionParser::NavigationResult::CalculateRelevance( | 235 int SearchSuggestionParser::NavigationResult::CalculateRelevance( |
| 233 const AutocompleteInput& input, | 236 const AutocompleteInput& input, |
| 234 bool keyword_provider_requested) const { | 237 bool keyword_provider_requested) const { |
| 235 return (from_keyword_provider_ || !keyword_provider_requested) ? 800 : 150; | 238 return (from_keyword_provider_ || !keyword_provider_requested) ? 800 : 150; |
| 236 } | 239 } |
| 237 | 240 |
| 238 // SearchSuggestionParser::Results --------------------------------------------- | 241 // SearchSuggestionParser::Results --------------------------------------------- |
| 239 | 242 |
| 240 SearchSuggestionParser::Results::Results() | 243 SearchSuggestionParser::Results::Results() |
| 241 : verbatim_relevance(-1), | 244 : verbatim_relevance(-1), |
| 242 field_trial_triggered(false) {} | 245 field_trial_triggered(false), |
| 246 relevances_from_server(false) {} |
| 243 | 247 |
| 244 SearchSuggestionParser::Results::~Results() {} | 248 SearchSuggestionParser::Results::~Results() {} |
| 245 | 249 |
| 246 void SearchSuggestionParser::Results::Clear() { | 250 void SearchSuggestionParser::Results::Clear() { |
| 247 suggest_results.clear(); | 251 suggest_results.clear(); |
| 248 navigation_results.clear(); | 252 navigation_results.clear(); |
| 249 verbatim_relevance = -1; | 253 verbatim_relevance = -1; |
| 250 metadata.clear(); | 254 metadata.clear(); |
| 251 } | 255 } |
| 252 | 256 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 267 if (i->relevance_from_server()) | 271 if (i->relevance_from_server()) |
| 268 return true; | 272 return true; |
| 269 } | 273 } |
| 270 | 274 |
| 271 return false; | 275 return false; |
| 272 } | 276 } |
| 273 | 277 |
| 274 // SearchSuggestionParser ------------------------------------------------------ | 278 // SearchSuggestionParser ------------------------------------------------------ |
| 275 | 279 |
| 276 // static | 280 // static |
| 281 std::string SearchSuggestionParser::ExtractJsonData( |
| 282 const net::URLFetcher* source) { |
| 283 const net::HttpResponseHeaders* const response_headers = |
| 284 source->GetResponseHeaders(); |
| 285 std::string json_data; |
| 286 source->GetResponseAsString(&json_data); |
| 287 |
| 288 // JSON is supposed to be UTF-8, but some suggest service providers send |
| 289 // JSON files in non-UTF-8 encodings. The actual encoding is usually |
| 290 // specified in the Content-Type header field. |
| 291 if (response_headers) { |
| 292 std::string charset; |
| 293 if (response_headers->GetCharset(&charset)) { |
| 294 base::string16 data_16; |
| 295 // TODO(jungshik): Switch to CodePageToUTF8 after it's added. |
| 296 if (base::CodepageToUTF16(json_data, charset.c_str(), |
| 297 base::OnStringConversionError::FAIL, |
| 298 &data_16)) |
| 299 json_data = base::UTF16ToUTF8(data_16); |
| 300 } |
| 301 } |
| 302 return json_data; |
| 303 } |
| 304 |
| 305 // static |
| 306 scoped_ptr<base::Value> SearchSuggestionParser::DeserializeJsonData( |
| 307 std::string json_data) { |
| 308 // The JSON response should be an array. |
| 309 for (size_t response_start_index = json_data.find("["), i = 0; |
| 310 response_start_index != std::string::npos && i < 5; |
| 311 response_start_index = json_data.find("[", 1), i++) { |
| 312 // Remove any XSSI guards to allow for JSON parsing. |
| 313 if (response_start_index > 0) |
| 314 json_data.erase(0, response_start_index); |
| 315 |
| 316 JSONStringValueSerializer deserializer(json_data); |
| 317 deserializer.set_allow_trailing_comma(true); |
| 318 int error_code = 0; |
| 319 scoped_ptr<base::Value> data(deserializer.Deserialize(&error_code, NULL)); |
| 320 if (error_code == 0) |
| 321 return data.Pass(); |
| 322 } |
| 323 return scoped_ptr<base::Value>(); |
| 324 } |
| 325 |
| 326 // static |
| 277 bool SearchSuggestionParser::ParseSuggestResults( | 327 bool SearchSuggestionParser::ParseSuggestResults( |
| 278 const base::Value& root_val, | 328 const base::Value& root_val, |
| 279 const AutocompleteInput& input, | 329 const AutocompleteInput& input, |
| 280 const AutocompleteSchemeClassifier& scheme_classifier, | 330 const AutocompleteSchemeClassifier& scheme_classifier, |
| 281 const ImagePrefetchCallback& image_prefetch_callback, | |
| 282 int default_result_relevance, | 331 int default_result_relevance, |
| 283 const std::string& languages, | 332 const std::string& languages, |
| 284 bool is_keyword_result, | 333 bool is_keyword_result, |
| 285 bool* relevances_from_server, | |
| 286 Results* results) { | 334 Results* results) { |
| 287 base::string16 query; | 335 base::string16 query; |
| 288 const base::ListValue* root_list = NULL; | 336 const base::ListValue* root_list = NULL; |
| 289 const base::ListValue* results_list = NULL; | 337 const base::ListValue* results_list = NULL; |
| 290 | 338 |
| 291 if (!root_val.GetAsList(&root_list) || !root_list->GetString(0, &query) || | 339 if (!root_val.GetAsList(&root_list) || !root_list->GetString(0, &query) || |
| 292 query != input.text() || !root_list->GetList(1, &results_list)) | 340 query != input.text() || !root_list->GetList(1, &results_list)) |
| 293 return false; | 341 return false; |
| 294 | 342 |
| 295 // 3rd element: Description list. | 343 // 3rd element: Description list. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 | 381 |
| 334 // Store the metadata that came with the response in case we need to pass it | 382 // Store the metadata that came with the response in case we need to pass it |
| 335 // along with the prefetch query to Instant. | 383 // along with the prefetch query to Instant. |
| 336 JSONStringValueSerializer json_serializer(&results->metadata); | 384 JSONStringValueSerializer json_serializer(&results->metadata); |
| 337 json_serializer.Serialize(*extras); | 385 json_serializer.Serialize(*extras); |
| 338 } | 386 } |
| 339 | 387 |
| 340 // Clear the previous results now that new results are available. | 388 // Clear the previous results now that new results are available. |
| 341 results->suggest_results.clear(); | 389 results->suggest_results.clear(); |
| 342 results->navigation_results.clear(); | 390 results->navigation_results.clear(); |
| 391 results->answers_image_urls.clear(); |
| 343 | 392 |
| 344 base::string16 suggestion; | 393 base::string16 suggestion; |
| 345 std::string type; | 394 std::string type; |
| 346 int relevance = default_result_relevance; | 395 int relevance = default_result_relevance; |
| 347 // Prohibit navsuggest in FORCED_QUERY mode. Users wants queries, not URLs. | 396 // Prohibit navsuggest in FORCED_QUERY mode. Users wants queries, not URLs. |
| 348 const bool allow_navsuggest = | 397 const bool allow_navsuggest = |
| 349 input.type() != metrics::OmniboxInputType::FORCED_QUERY; | 398 input.type() != metrics::OmniboxInputType::FORCED_QUERY; |
| 350 const base::string16& trimmed_input = | 399 const base::string16& trimmed_input = |
| 351 base::CollapseWhitespace(input.text(), false); | 400 base::CollapseWhitespace(input.text(), false); |
| 352 for (size_t index = 0; results_list->GetString(index, &suggestion); ++index) { | 401 for (size_t index = 0; results_list->GetString(index, &suggestion); ++index) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 // Error correction for bad data from server. | 448 // Error correction for bad data from server. |
| 400 if (match_contents.empty()) | 449 if (match_contents.empty()) |
| 401 match_contents = suggestion; | 450 match_contents = suggestion; |
| 402 suggestion_detail->GetString("a", &annotation); | 451 suggestion_detail->GetString("a", &annotation); |
| 403 suggestion_detail->GetString("q", &suggest_query_params); | 452 suggestion_detail->GetString("q", &suggest_query_params); |
| 404 | 453 |
| 405 // Extract Answers, if provided. | 454 // Extract Answers, if provided. |
| 406 const base::DictionaryValue* answer_json = NULL; | 455 const base::DictionaryValue* answer_json = NULL; |
| 407 if (suggestion_detail->GetDictionary("ansa", &answer_json)) { | 456 if (suggestion_detail->GetDictionary("ansa", &answer_json)) { |
| 408 match_type = AutocompleteMatchType::SEARCH_SUGGEST_ANSWER; | 457 match_type = AutocompleteMatchType::SEARCH_SUGGEST_ANSWER; |
| 409 if (!image_prefetch_callback.is_null()) | 458 GetAnswersImageURLs(answer_json, &results->answers_image_urls); |
| 410 PrefetchAnswersImages(answer_json, image_prefetch_callback); | |
| 411 std::string contents; | 459 std::string contents; |
| 412 base::JSONWriter::Write(answer_json, &contents); | 460 base::JSONWriter::Write(answer_json, &contents); |
| 413 answer_contents = base::UTF8ToUTF16(contents); | 461 answer_contents = base::UTF8ToUTF16(contents); |
| 414 suggestion_detail->GetString("ansb", &answer_type); | 462 suggestion_detail->GetString("ansb", &answer_type); |
| 415 } | 463 } |
| 416 } | 464 } |
| 417 } | 465 } |
| 418 | 466 |
| 419 bool should_prefetch = static_cast<int>(index) == prefetch_index; | 467 bool should_prefetch = static_cast<int>(index) == prefetch_index; |
| 420 // TODO(kochi): Improve calculator suggestion presentation. | 468 // TODO(kochi): Improve calculator suggestion presentation. |
| 421 results->suggest_results.push_back(SuggestResult( | 469 results->suggest_results.push_back(SuggestResult( |
| 422 base::CollapseWhitespace(suggestion, false), match_type, | 470 base::CollapseWhitespace(suggestion, false), match_type, |
| 423 base::CollapseWhitespace(match_contents, false), | 471 base::CollapseWhitespace(match_contents, false), |
| 424 match_contents_prefix, annotation, answer_contents, answer_type, | 472 match_contents_prefix, annotation, answer_contents, answer_type, |
| 425 suggest_query_params, deletion_url, is_keyword_result, relevance, | 473 suggest_query_params, deletion_url, is_keyword_result, relevance, |
| 426 relevances != NULL, should_prefetch, trimmed_input)); | 474 relevances != NULL, should_prefetch, trimmed_input)); |
| 427 } | 475 } |
| 428 } | 476 } |
| 429 *relevances_from_server = relevances != NULL; | 477 results->relevances_from_server = relevances != NULL; |
| 430 return true; | 478 return true; |
| 431 } | 479 } |
| 432 | 480 |
| 433 // static | 481 // static |
| 434 void SearchSuggestionParser::PrefetchAnswersImages( | 482 void SearchSuggestionParser::GetAnswersImageURLs( |
| 435 const base::DictionaryValue* answer_json, | 483 const base::DictionaryValue* answer_json, |
| 436 const ImagePrefetchCallback& image_prefetch_callback) { | 484 std::vector<GURL>* urls) { |
| 437 DCHECK(answer_json); | 485 DCHECK(answer_json); |
| 438 DCHECK(!image_prefetch_callback.is_null()); | |
| 439 const base::ListValue* lines = NULL; | 486 const base::ListValue* lines = NULL; |
| 440 answer_json->GetList("l", &lines); | 487 answer_json->GetList("l", &lines); |
| 441 if (!lines || lines->GetSize() == 0) | 488 if (!lines || lines->GetSize() == 0) |
| 442 return; | 489 return; |
| 443 | 490 |
| 444 for (size_t line = 0; line < lines->GetSize(); ++line) { | 491 for (size_t line = 0; line < lines->GetSize(); ++line) { |
| 445 const base::DictionaryValue* imageLine = NULL; | 492 const base::DictionaryValue* imageLine = NULL; |
| 446 lines->GetDictionary(line, &imageLine); | 493 lines->GetDictionary(line, &imageLine); |
| 447 if (!imageLine) | 494 if (!imageLine) |
| 448 continue; | 495 continue; |
| 449 const base::DictionaryValue* imageData = NULL; | 496 const base::DictionaryValue* imageData = NULL; |
| 450 imageLine->GetDictionary("i", &imageData); | 497 imageLine->GetDictionary("i", &imageData); |
| 451 if (!imageData) | 498 if (!imageData) |
| 452 continue; | 499 continue; |
| 453 std::string imageUrl; | 500 std::string imageUrl; |
| 454 imageData->GetString("d", &imageUrl); | 501 imageData->GetString("d", &imageUrl); |
| 455 image_prefetch_callback.Run(GURL(imageUrl)); | 502 urls->push_back(GURL(imageUrl)); |
| 456 } | 503 } |
| 457 } | 504 } |
| OLD | NEW |