Index: components/autocomplete/search_suggestion_parser.cc |
diff --git a/components/autocomplete/search_suggestion_parser.cc b/components/autocomplete/search_suggestion_parser.cc |
deleted file mode 100644 |
index 2f72b28e6d8b88298c01f72577b83fbcdbd9aeca..0000000000000000000000000000000000000000 |
--- a/components/autocomplete/search_suggestion_parser.cc |
+++ /dev/null |
@@ -1,504 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "components/autocomplete/search_suggestion_parser.h" |
- |
-#include "base/i18n/icu_string_conversions.h" |
-#include "base/json/json_string_value_serializer.h" |
-#include "base/json/json_writer.h" |
-#include "base/logging.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "base/values.h" |
-#include "components/autocomplete/autocomplete_input.h" |
-#include "components/autocomplete/url_prefix.h" |
-#include "components/url_fixer/url_fixer.h" |
-#include "net/base/net_util.h" |
-#include "net/http/http_response_headers.h" |
-#include "net/url_request/url_fetcher.h" |
- |
-namespace { |
- |
-AutocompleteMatchType::Type GetAutocompleteMatchType(const std::string& type) { |
- if (type == "ENTITY") |
- return AutocompleteMatchType::SEARCH_SUGGEST_ENTITY; |
- if (type == "INFINITE") |
- return AutocompleteMatchType::SEARCH_SUGGEST_INFINITE; |
- if (type == "PERSONALIZED_QUERY") |
- return AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED; |
- if (type == "PROFILE") |
- return AutocompleteMatchType::SEARCH_SUGGEST_PROFILE; |
- if (type == "NAVIGATION") |
- return AutocompleteMatchType::NAVSUGGEST; |
- if (type == "PERSONALIZED_NAVIGATION") |
- return AutocompleteMatchType::NAVSUGGEST_PERSONALIZED; |
- return AutocompleteMatchType::SEARCH_SUGGEST; |
-} |
- |
-} // namespace |
- |
-// SearchSuggestionParser::Result ---------------------------------------------- |
- |
-SearchSuggestionParser::Result::Result(bool from_keyword_provider, |
- int relevance, |
- bool relevance_from_server, |
- AutocompleteMatchType::Type type, |
- const std::string& deletion_url) |
- : from_keyword_provider_(from_keyword_provider), |
- type_(type), |
- relevance_(relevance), |
- relevance_from_server_(relevance_from_server), |
- deletion_url_(deletion_url) {} |
- |
-SearchSuggestionParser::Result::~Result() {} |
- |
-// SearchSuggestionParser::SuggestResult --------------------------------------- |
- |
-SearchSuggestionParser::SuggestResult::SuggestResult( |
- const base::string16& suggestion, |
- AutocompleteMatchType::Type type, |
- const base::string16& match_contents, |
- const base::string16& match_contents_prefix, |
- const base::string16& annotation, |
- const base::string16& answer_contents, |
- const base::string16& answer_type, |
- const std::string& suggest_query_params, |
- const std::string& deletion_url, |
- bool from_keyword_provider, |
- int relevance, |
- bool relevance_from_server, |
- bool should_prefetch, |
- const base::string16& input_text) |
- : Result(from_keyword_provider, |
- relevance, |
- relevance_from_server, |
- type, |
- deletion_url), |
- suggestion_(suggestion), |
- match_contents_prefix_(match_contents_prefix), |
- annotation_(annotation), |
- suggest_query_params_(suggest_query_params), |
- answer_contents_(answer_contents), |
- answer_type_(answer_type), |
- should_prefetch_(should_prefetch) { |
- match_contents_ = match_contents; |
- DCHECK(!match_contents_.empty()); |
- ClassifyMatchContents(true, input_text); |
-} |
- |
-SearchSuggestionParser::SuggestResult::~SuggestResult() {} |
- |
-void SearchSuggestionParser::SuggestResult::ClassifyMatchContents( |
- const bool allow_bolding_all, |
- const base::string16& input_text) { |
- if (input_text.empty()) { |
- // In case of zero-suggest results, do not highlight matches. |
- match_contents_class_.push_back( |
- ACMatchClassification(0, ACMatchClassification::NONE)); |
- return; |
- } |
- |
- base::string16 lookup_text = input_text; |
- if (type_ == AutocompleteMatchType::SEARCH_SUGGEST_INFINITE) { |
- const size_t contents_index = |
- suggestion_.length() - match_contents_.length(); |
- // Ensure the query starts with the input text, and ends with the match |
- // contents, and the input text has an overlap with contents. |
- if (StartsWith(suggestion_, input_text, true) && |
- EndsWith(suggestion_, match_contents_, true) && |
- (input_text.length() > contents_index)) { |
- lookup_text = input_text.substr(contents_index); |
- } |
- } |
- size_t lookup_position = match_contents_.find(lookup_text); |
- if (!allow_bolding_all && (lookup_position == base::string16::npos)) { |
- // Bail if the code below to update the bolding would bold the whole |
- // string. Note that the string may already be entirely bolded; if |
- // so, leave it as is. |
- return; |
- } |
- match_contents_class_.clear(); |
- // We do intra-string highlighting for suggestions - the suggested segment |
- // will be highlighted, e.g. for input_text = "you" the suggestion may be |
- // "youtube", so we'll bold the "tube" section: you*tube*. |
- if (input_text != match_contents_) { |
- if (lookup_position == base::string16::npos) { |
- // The input text is not a substring of the query string, e.g. input |
- // text is "slasdot" and the query string is "slashdot", so we bold the |
- // whole thing. |
- match_contents_class_.push_back( |
- ACMatchClassification(0, ACMatchClassification::MATCH)); |
- } else { |
- // We don't iterate over the string here annotating all matches because |
- // it looks odd to have every occurrence of a substring that may be as |
- // short as a single character highlighted in a query suggestion result, |
- // e.g. for input text "s" and query string "southwest airlines", it |
- // looks odd if both the first and last s are highlighted. |
- if (lookup_position != 0) { |
- match_contents_class_.push_back( |
- ACMatchClassification(0, ACMatchClassification::MATCH)); |
- } |
- match_contents_class_.push_back( |
- ACMatchClassification(lookup_position, ACMatchClassification::NONE)); |
- size_t next_fragment_position = lookup_position + lookup_text.length(); |
- if (next_fragment_position < match_contents_.length()) { |
- match_contents_class_.push_back(ACMatchClassification( |
- next_fragment_position, ACMatchClassification::MATCH)); |
- } |
- } |
- } else { |
- // Otherwise, match_contents_ is a verbatim (what-you-typed) match, either |
- // for the default provider or a keyword search provider. |
- match_contents_class_.push_back( |
- ACMatchClassification(0, ACMatchClassification::NONE)); |
- } |
-} |
- |
-int SearchSuggestionParser::SuggestResult::CalculateRelevance( |
- const AutocompleteInput& input, |
- bool keyword_provider_requested) const { |
- if (!from_keyword_provider_ && keyword_provider_requested) |
- return 100; |
- return ((input.type() == metrics::OmniboxInputType::URL) ? 300 : 600); |
-} |
- |
-// SearchSuggestionParser::NavigationResult ------------------------------------ |
- |
-SearchSuggestionParser::NavigationResult::NavigationResult( |
- const AutocompleteSchemeClassifier& scheme_classifier, |
- const GURL& url, |
- AutocompleteMatchType::Type type, |
- const base::string16& description, |
- const std::string& deletion_url, |
- bool from_keyword_provider, |
- int relevance, |
- bool relevance_from_server, |
- const base::string16& input_text, |
- const std::string& languages) |
- : Result(from_keyword_provider, relevance, relevance_from_server, type, |
- deletion_url), |
- url_(url), |
- formatted_url_(AutocompleteInput::FormattedStringWithEquivalentMeaning( |
- url, net::FormatUrl(url, languages, |
- net::kFormatUrlOmitAll & ~net::kFormatUrlOmitHTTP, |
- net::UnescapeRule::SPACES, NULL, NULL, NULL), |
- scheme_classifier)), |
- description_(description) { |
- DCHECK(url_.is_valid()); |
- CalculateAndClassifyMatchContents(true, input_text, languages); |
-} |
- |
-SearchSuggestionParser::NavigationResult::~NavigationResult() {} |
- |
-void |
-SearchSuggestionParser::NavigationResult::CalculateAndClassifyMatchContents( |
- const bool allow_bolding_nothing, |
- const base::string16& input_text, |
- const std::string& languages) { |
- if (input_text.empty()) { |
- // In case of zero-suggest results, do not highlight matches. |
- match_contents_class_.push_back( |
- ACMatchClassification(0, ACMatchClassification::NONE)); |
- return; |
- } |
- |
- // First look for the user's input inside the formatted url as it would be |
- // without trimming the scheme, so we can find matches at the beginning of the |
- // scheme. |
- const URLPrefix* prefix = |
- URLPrefix::BestURLPrefix(formatted_url_, input_text); |
- size_t match_start = (prefix == NULL) ? |
- formatted_url_.find(input_text) : prefix->prefix.length(); |
- bool trim_http = !AutocompleteInput::HasHTTPScheme(input_text) && |
- (!prefix || (match_start != 0)); |
- const net::FormatUrlTypes format_types = |
- net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP); |
- |
- base::string16 match_contents = net::FormatUrl(url_, languages, format_types, |
- net::UnescapeRule::SPACES, NULL, NULL, &match_start); |
- // If the first match in the untrimmed string was inside a scheme that we |
- // trimmed, look for a subsequent match. |
- if (match_start == base::string16::npos) |
- match_start = match_contents.find(input_text); |
- // Update |match_contents_| and |match_contents_class_| if it's allowed. |
- if (allow_bolding_nothing || (match_start != base::string16::npos)) { |
- match_contents_ = match_contents; |
- // Safe if |match_start| is npos; also safe if the input is longer than the |
- // remaining contents after |match_start|. |
- AutocompleteMatch::ClassifyLocationInString(match_start, |
- input_text.length(), match_contents_.length(), |
- ACMatchClassification::URL, &match_contents_class_); |
- } |
-} |
- |
-int SearchSuggestionParser::NavigationResult::CalculateRelevance( |
- const AutocompleteInput& input, |
- bool keyword_provider_requested) const { |
- return (from_keyword_provider_ || !keyword_provider_requested) ? 800 : 150; |
-} |
- |
-// SearchSuggestionParser::Results --------------------------------------------- |
- |
-SearchSuggestionParser::Results::Results() |
- : verbatim_relevance(-1), |
- field_trial_triggered(false), |
- relevances_from_server(false) {} |
- |
-SearchSuggestionParser::Results::~Results() {} |
- |
-void SearchSuggestionParser::Results::Clear() { |
- suggest_results.clear(); |
- navigation_results.clear(); |
- verbatim_relevance = -1; |
- metadata.clear(); |
-} |
- |
-bool SearchSuggestionParser::Results::HasServerProvidedScores() const { |
- if (verbatim_relevance >= 0) |
- return true; |
- |
- // Right now either all results of one type will be server-scored or they will |
- // all be locally scored, but in case we change this later, we'll just check |
- // them all. |
- for (SuggestResults::const_iterator i(suggest_results.begin()); |
- i != suggest_results.end(); ++i) { |
- if (i->relevance_from_server()) |
- return true; |
- } |
- for (NavigationResults::const_iterator i(navigation_results.begin()); |
- i != navigation_results.end(); ++i) { |
- if (i->relevance_from_server()) |
- return true; |
- } |
- |
- return false; |
-} |
- |
-// SearchSuggestionParser ------------------------------------------------------ |
- |
-// static |
-std::string SearchSuggestionParser::ExtractJsonData( |
- const net::URLFetcher* source) { |
- const net::HttpResponseHeaders* const response_headers = |
- source->GetResponseHeaders(); |
- std::string json_data; |
- source->GetResponseAsString(&json_data); |
- |
- // JSON is supposed to be UTF-8, but some suggest service providers send |
- // JSON files in non-UTF-8 encodings. The actual encoding is usually |
- // specified in the Content-Type header field. |
- if (response_headers) { |
- std::string charset; |
- if (response_headers->GetCharset(&charset)) { |
- base::string16 data_16; |
- // TODO(jungshik): Switch to CodePageToUTF8 after it's added. |
- if (base::CodepageToUTF16(json_data, charset.c_str(), |
- base::OnStringConversionError::FAIL, |
- &data_16)) |
- json_data = base::UTF16ToUTF8(data_16); |
- } |
- } |
- return json_data; |
-} |
- |
-// static |
-scoped_ptr<base::Value> SearchSuggestionParser::DeserializeJsonData( |
- std::string json_data) { |
- // The JSON response should be an array. |
- for (size_t response_start_index = json_data.find("["), i = 0; |
- response_start_index != std::string::npos && i < 5; |
- response_start_index = json_data.find("[", 1), i++) { |
- // Remove any XSSI guards to allow for JSON parsing. |
- if (response_start_index > 0) |
- json_data.erase(0, response_start_index); |
- |
- JSONStringValueSerializer deserializer(json_data); |
- deserializer.set_allow_trailing_comma(true); |
- int error_code = 0; |
- scoped_ptr<base::Value> data(deserializer.Deserialize(&error_code, NULL)); |
- if (error_code == 0) |
- return data.Pass(); |
- } |
- return scoped_ptr<base::Value>(); |
-} |
- |
-// static |
-bool SearchSuggestionParser::ParseSuggestResults( |
- const base::Value& root_val, |
- const AutocompleteInput& input, |
- const AutocompleteSchemeClassifier& scheme_classifier, |
- int default_result_relevance, |
- const std::string& languages, |
- bool is_keyword_result, |
- Results* results) { |
- base::string16 query; |
- const base::ListValue* root_list = NULL; |
- const base::ListValue* results_list = NULL; |
- |
- if (!root_val.GetAsList(&root_list) || !root_list->GetString(0, &query) || |
- query != input.text() || !root_list->GetList(1, &results_list)) |
- return false; |
- |
- // 3rd element: Description list. |
- const base::ListValue* descriptions = NULL; |
- root_list->GetList(2, &descriptions); |
- |
- // 4th element: Disregard the query URL list for now. |
- |
- // Reset suggested relevance information. |
- results->verbatim_relevance = -1; |
- |
- // 5th element: Optional key-value pairs from the Suggest server. |
- const base::ListValue* types = NULL; |
- const base::ListValue* relevances = NULL; |
- const base::ListValue* suggestion_details = NULL; |
- const base::DictionaryValue* extras = NULL; |
- int prefetch_index = -1; |
- if (root_list->GetDictionary(4, &extras)) { |
- extras->GetList("google:suggesttype", &types); |
- |
- // Discard this list if its size does not match that of the suggestions. |
- if (extras->GetList("google:suggestrelevance", &relevances) && |
- (relevances->GetSize() != results_list->GetSize())) |
- relevances = NULL; |
- extras->GetInteger("google:verbatimrelevance", |
- &results->verbatim_relevance); |
- |
- // Check if the active suggest field trial (if any) has triggered either |
- // for the default provider or keyword provider. |
- results->field_trial_triggered = false; |
- extras->GetBoolean("google:fieldtrialtriggered", |
- &results->field_trial_triggered); |
- |
- const base::DictionaryValue* client_data = NULL; |
- if (extras->GetDictionary("google:clientdata", &client_data) && client_data) |
- client_data->GetInteger("phi", &prefetch_index); |
- |
- if (extras->GetList("google:suggestdetail", &suggestion_details) && |
- suggestion_details->GetSize() != results_list->GetSize()) |
- suggestion_details = NULL; |
- |
- // Store the metadata that came with the response in case we need to pass it |
- // along with the prefetch query to Instant. |
- JSONStringValueSerializer json_serializer(&results->metadata); |
- json_serializer.Serialize(*extras); |
- } |
- |
- // Clear the previous results now that new results are available. |
- results->suggest_results.clear(); |
- results->navigation_results.clear(); |
- results->answers_image_urls.clear(); |
- |
- base::string16 suggestion; |
- std::string type; |
- int relevance = default_result_relevance; |
- // Prohibit navsuggest in FORCED_QUERY mode. Users wants queries, not URLs. |
- const bool allow_navsuggest = |
- input.type() != metrics::OmniboxInputType::FORCED_QUERY; |
- const base::string16& trimmed_input = |
- base::CollapseWhitespace(input.text(), false); |
- for (size_t index = 0; results_list->GetString(index, &suggestion); ++index) { |
- // Google search may return empty suggestions for weird input characters, |
- // they make no sense at all and can cause problems in our code. |
- if (suggestion.empty()) |
- continue; |
- |
- // Apply valid suggested relevance scores; discard invalid lists. |
- if (relevances != NULL && !relevances->GetInteger(index, &relevance)) |
- relevances = NULL; |
- AutocompleteMatchType::Type match_type = |
- AutocompleteMatchType::SEARCH_SUGGEST; |
- if (types && types->GetString(index, &type)) |
- match_type = GetAutocompleteMatchType(type); |
- const base::DictionaryValue* suggestion_detail = NULL; |
- std::string deletion_url; |
- |
- if (suggestion_details && |
- suggestion_details->GetDictionary(index, &suggestion_detail)) |
- suggestion_detail->GetString("du", &deletion_url); |
- |
- if ((match_type == AutocompleteMatchType::NAVSUGGEST) || |
- (match_type == AutocompleteMatchType::NAVSUGGEST_PERSONALIZED)) { |
- // Do not blindly trust the URL coming from the server to be valid. |
- GURL url( |
- url_fixer::FixupURL(base::UTF16ToUTF8(suggestion), std::string())); |
- if (url.is_valid() && allow_navsuggest) { |
- base::string16 title; |
- if (descriptions != NULL) |
- descriptions->GetString(index, &title); |
- results->navigation_results.push_back(NavigationResult( |
- scheme_classifier, url, match_type, title, deletion_url, |
- is_keyword_result, relevance, relevances != NULL, input.text(), |
- languages)); |
- } |
- } else { |
- base::string16 match_contents = suggestion; |
- base::string16 match_contents_prefix; |
- base::string16 annotation; |
- base::string16 answer_contents; |
- base::string16 answer_type; |
- std::string suggest_query_params; |
- |
- if (suggestion_details) { |
- suggestion_details->GetDictionary(index, &suggestion_detail); |
- if (suggestion_detail) { |
- suggestion_detail->GetString("t", &match_contents); |
- suggestion_detail->GetString("mp", &match_contents_prefix); |
- // Error correction for bad data from server. |
- if (match_contents.empty()) |
- match_contents = suggestion; |
- suggestion_detail->GetString("a", &annotation); |
- suggestion_detail->GetString("q", &suggest_query_params); |
- |
- // Extract Answers, if provided. |
- const base::DictionaryValue* answer_json = NULL; |
- if (suggestion_detail->GetDictionary("ansa", &answer_json)) { |
- match_type = AutocompleteMatchType::SEARCH_SUGGEST_ANSWER; |
- GetAnswersImageURLs(answer_json, &results->answers_image_urls); |
- std::string contents; |
- base::JSONWriter::Write(answer_json, &contents); |
- answer_contents = base::UTF8ToUTF16(contents); |
- suggestion_detail->GetString("ansb", &answer_type); |
- } |
- } |
- } |
- |
- bool should_prefetch = static_cast<int>(index) == prefetch_index; |
- // TODO(kochi): Improve calculator suggestion presentation. |
- results->suggest_results.push_back(SuggestResult( |
- base::CollapseWhitespace(suggestion, false), match_type, |
- base::CollapseWhitespace(match_contents, false), |
- match_contents_prefix, annotation, answer_contents, answer_type, |
- suggest_query_params, deletion_url, is_keyword_result, relevance, |
- relevances != NULL, should_prefetch, trimmed_input)); |
- } |
- } |
- results->relevances_from_server = relevances != NULL; |
- return true; |
-} |
- |
-// static |
-void SearchSuggestionParser::GetAnswersImageURLs( |
- const base::DictionaryValue* answer_json, |
- std::vector<GURL>* urls) { |
- DCHECK(answer_json); |
- const base::ListValue* lines = NULL; |
- answer_json->GetList("l", &lines); |
- if (!lines || lines->GetSize() == 0) |
- return; |
- |
- for (size_t line = 0; line < lines->GetSize(); ++line) { |
- const base::DictionaryValue* imageLine = NULL; |
- lines->GetDictionary(line, &imageLine); |
- if (!imageLine) |
- continue; |
- const base::DictionaryValue* imageData = NULL; |
- imageLine->GetDictionary("i", &imageData); |
- if (!imageData) |
- continue; |
- std::string imageUrl; |
- imageData->GetString("d", &imageUrl); |
- urls->push_back(GURL(imageUrl)); |
- } |
-} |