diff options
author | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-30 11:58:37 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-30 11:58:37 +0000 |
commit | 0b9575f6161a2e382bbc2c77484a01fe8a4b73f3 (patch) | |
tree | b0ee63027ed74980ef54bfb53f588f4dd0d7100d | |
parent | 3cb7c2b55cfa24ae40cd49a1d8fd15275b55dd0b (diff) | |
download | chromium_src-0b9575f6161a2e382bbc2c77484a01fe8a4b73f3.zip chromium_src-0b9575f6161a2e382bbc2c77484a01fe8a4b73f3.tar.gz chromium_src-0b9575f6161a2e382bbc2c77484a01fe8a4b73f3.tar.bz2 |
Split BaseSearchProvider's server response parser code as a separate class
Add a new class SearchSuggestionParser.
Move structs (Result, SuggestResult, NavigationResult and results) from BaseSearchProvider to SearchSuggestionParser.
Move functions (ParseSuggestReulsts() and PrefetchAnswersImages()) to SearchSuggestionParser.
BUG=388515
TEST=build
Review URL: https://codereview.chromium.org/417143002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286489 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/autocomplete/base_search_provider.cc | 470 | ||||
-rw-r--r-- | chrome/browser/autocomplete/base_search_provider.h | 241 | ||||
-rw-r--r-- | chrome/browser/autocomplete/search_provider.cc | 84 | ||||
-rw-r--r-- | chrome/browser/autocomplete/search_provider.h | 53 | ||||
-rw-r--r-- | chrome/browser/autocomplete/search_provider_unittest.cc | 8 | ||||
-rw-r--r-- | chrome/browser/autocomplete/zero_suggest_provider.cc | 17 | ||||
-rw-r--r-- | chrome/browser/autocomplete/zero_suggest_provider.h | 14 | ||||
-rw-r--r-- | components/autocomplete.gypi | 2 | ||||
-rw-r--r-- | components/autocomplete/search_suggestion_parser.cc | 457 | ||||
-rw-r--r-- | components/autocomplete/search_suggestion_parser.h | 279 |
10 files changed, 876 insertions, 749 deletions
diff --git a/chrome/browser/autocomplete/base_search_provider.cc b/chrome/browser/autocomplete/base_search_provider.cc index 17e9b25..2e1458c 100644 --- a/chrome/browser/autocomplete/base_search_provider.cc +++ b/chrome/browser/autocomplete/base_search_provider.cc @@ -7,7 +7,6 @@ #include "base/i18n/case_conversion.h" #include "base/i18n/icu_string_conversions.h" #include "base/json/json_string_value_serializer.h" -#include "base/json/json_writer.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/strings/string_util.h" @@ -26,17 +25,14 @@ #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/common/pref_names.h" -#include "components/autocomplete/url_prefix.h" #include "components/metrics/proto/omnibox_event.pb.h" #include "components/metrics/proto/omnibox_input_type.pb.h" #include "components/search_engines/template_url.h" #include "components/search_engines/template_url_prepopulate_data.h" #include "components/search_engines/template_url_service.h" #include "components/sync_driver/sync_prefs.h" -#include "components/url_fixer/url_fixer.h" #include "content/public/common/url_constants.h" #include "net/base/escape.h" -#include "net/base/net_util.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/http/http_response_headers.h" #include "net/url_request/url_fetcher.h" @@ -45,26 +41,6 @@ using metrics::OmniboxEventProto; -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 - // SuggestionDeletionHandler ------------------------------------------------- // This class handles making requests to the server in order to delete @@ -149,7 +125,7 @@ AutocompleteMatch BaseSearchProvider::CreateSearchSuggestion( const TemplateURL* template_url, const SearchTermsData& search_terms_data) { return CreateSearchSuggestion( - NULL, AutocompleteInput(), BaseSearchProvider::SuggestResult( + NULL, AutocompleteInput(), SearchSuggestionParser::SuggestResult( suggestion, type, suggestion, base::string16(), base::string16(), base::string16(), base::string16(), std::string(), std::string(), from_keyword_provider, 0, false, false, base::string16()), @@ -220,239 +196,6 @@ const char BaseSearchProvider::kFalse[] = "false"; BaseSearchProvider::~BaseSearchProvider() {} -// BaseSearchProvider::Result -------------------------------------------------- - -BaseSearchProvider::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) {} - -BaseSearchProvider::Result::~Result() {} - -// BaseSearchProvider::SuggestResult ------------------------------------------- - -BaseSearchProvider::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); -} - -BaseSearchProvider::SuggestResult::~SuggestResult() {} - -void BaseSearchProvider::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 BaseSearchProvider::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); -} - -// BaseSearchProvider::NavigationResult ---------------------------------------- - -BaseSearchProvider::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); -} - -BaseSearchProvider::NavigationResult::~NavigationResult() {} - -void BaseSearchProvider::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 BaseSearchProvider::NavigationResult::CalculateRelevance( - const AutocompleteInput& input, - bool keyword_provider_requested) const { - return (from_keyword_provider_ || !keyword_provider_requested) ? 800 : 150; -} - -// BaseSearchProvider::Results ------------------------------------------------- - -BaseSearchProvider::Results::Results() : verbatim_relevance(-1) {} - -BaseSearchProvider::Results::~Results() {} - -void BaseSearchProvider::Results::Clear() { - suggest_results.clear(); - navigation_results.clear(); - verbatim_relevance = -1; - metadata.clear(); -} - -bool BaseSearchProvider::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; -} - void BaseSearchProvider::SetDeletionURL(const std::string& deletion_url, AutocompleteMatch* match) { if (deletion_url.empty()) @@ -471,13 +214,11 @@ void BaseSearchProvider::SetDeletionURL(const std::string& deletion_url, } } -// BaseSearchProvider --------------------------------------------------------- - // static AutocompleteMatch BaseSearchProvider::CreateSearchSuggestion( AutocompleteProvider* autocomplete_provider, const AutocompleteInput& input, - const SuggestResult& suggestion, + const SearchSuggestionParser::SuggestResult& suggestion, const TemplateURL* template_url, const SearchTermsData& search_terms_data, int accepted_suggestion, @@ -704,11 +445,12 @@ void BaseSearchProvider::OnURLFetchComplete(const net::URLFetcher* source) { listener_->OnProviderUpdate(results_updated); } -void BaseSearchProvider::AddMatchToMap(const SuggestResult& result, - const std::string& metadata, - int accepted_suggestion, - bool mark_as_deletable, - MatchMap* map) { +void BaseSearchProvider::AddMatchToMap( + const SearchSuggestionParser::SuggestResult& result, + const std::string& metadata, + int accepted_suggestion, + bool mark_as_deletable, + MatchMap* map) { AutocompleteMatch match = CreateSearchSuggestion( this, GetInput(result.from_keyword_provider()), result, GetTemplateURL(result.from_keyword_provider()), @@ -780,188 +522,34 @@ void BaseSearchProvider::AddMatchToMap(const SuggestResult& result, } } -bool BaseSearchProvider::ParseSuggestResults(const base::Value& root_val, - bool is_keyword_result, - Results* results) { - base::string16 query; - const base::ListValue* root_list = NULL; - const base::ListValue* results_list = NULL; - const AutocompleteInput& input = GetInput(is_keyword_result); - - 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. - bool triggered = false; - extras->GetBoolean("google:fieldtrialtriggered", &triggered); - field_trial_triggered_ |= triggered; - field_trial_triggered_in_session_ |= 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(); - - base::string16 suggestion; - std::string type; - int relevance = GetDefaultResultRelevance(); - // Prohibit navsuggest in FORCED_QUERY mode. Users wants queries, not URLs. - const bool allow_navsuggest = - input.type() != metrics::OmniboxInputType::FORCED_QUERY; - const std::string languages( - profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); - 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( - ChromeAutocompleteSchemeClassifier(profile_), 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; - PrefetchAnswersImages(answer_json); - 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)); - } - } - SortResults(is_keyword_result, relevances, results); - return true; -} - -void BaseSearchProvider::PrefetchAnswersImages( - const base::DictionaryValue* answer_json) { - DCHECK(answer_json); - const base::ListValue* lines = NULL; - answer_json->GetList("l", &lines); - if (!lines || lines->GetSize() == 0) - return; - +bool BaseSearchProvider::ParseSuggestResults( + const base::Value& root_val, + bool is_keyword_result, + SearchSuggestionParser::Results* results) { BitmapFetcherService* image_service = BitmapFetcherServiceFactory::GetForBrowserContext(profile_); DCHECK(image_service); - 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); - image_service->Prefetch(GURL(imageUrl)); - } + bool relevances_from_server = false; + if (!SearchSuggestionParser::ParseSuggestResults( + root_val, GetInput(is_keyword_result), + ChromeAutocompleteSchemeClassifier(profile_), + base::Bind(&BitmapFetcherService::Prefetch, + base::Unretained(image_service)), + GetDefaultResultRelevance(), + profile_->GetPrefs()->GetString(prefs::kAcceptLanguages), + is_keyword_result, &relevances_from_server, results)) + return false; + + field_trial_triggered_ |= results->field_trial_triggered; + field_trial_triggered_in_session_ |= results->field_trial_triggered; + SortResults(is_keyword_result, relevances_from_server, results); + return true; } void BaseSearchProvider::SortResults(bool is_keyword, - const base::ListValue* relevances, - Results* results) { + bool relevances_from_server, + SearchSuggestionParser::Results* results) { } bool BaseSearchProvider::StoreSuggestionResponse( diff --git a/chrome/browser/autocomplete/base_search_provider.h b/chrome/browser/autocomplete/base_search_provider.h index ced91f2..cc6517d 100644 --- a/chrome/browser/autocomplete/base_search_provider.h +++ b/chrome/browser/autocomplete/base_search_provider.h @@ -19,6 +19,7 @@ #include "components/autocomplete/autocomplete_input.h" #include "components/autocomplete/autocomplete_match.h" #include "components/autocomplete/autocomplete_provider.h" +#include "components/autocomplete/search_suggestion_parser.h" #include "components/metrics/proto/omnibox_event.pb.h" #include "net/url_request/url_fetcher_delegate.h" @@ -99,231 +100,10 @@ class BaseSearchProvider : public AutocompleteProvider, virtual ~BaseSearchProvider(); - // The Result classes are intermediate representations of AutocompleteMatches, - // simply containing relevance-ranked search and navigation suggestions. - // They may be cached to provide some synchronous matches while requests for - // new suggestions from updated input are in flight. - // TODO(msw) Extend these classes to generate their corresponding matches and - // other requisite data, in order to consolidate and simplify the - // highly fragmented SearchProvider logic for each Result type. - class Result { - public: - Result(bool from_keyword_provider, - int relevance, - bool relevance_from_server, - AutocompleteMatchType::Type type, - const std::string& deletion_url); - virtual ~Result(); - - bool from_keyword_provider() const { return from_keyword_provider_; } - - const base::string16& match_contents() const { return match_contents_; } - const ACMatchClassifications& match_contents_class() const { - return match_contents_class_; - } - - AutocompleteMatchType::Type type() const { return type_; } - int relevance() const { return relevance_; } - void set_relevance(int relevance) { relevance_ = relevance; } - - bool relevance_from_server() const { return relevance_from_server_; } - void set_relevance_from_server(bool relevance_from_server) { - relevance_from_server_ = relevance_from_server; - } - - const std::string& deletion_url() const { return deletion_url_; } - - // Returns the default relevance value for this result (which may - // be left over from a previous omnibox input) given the current - // input and whether the current input caused a keyword provider - // to be active. - virtual int CalculateRelevance(const AutocompleteInput& input, - bool keyword_provider_requested) const = 0; - - protected: - // The contents to be displayed and its style info. - base::string16 match_contents_; - ACMatchClassifications match_contents_class_; - - // True if the result came from the keyword provider. - bool from_keyword_provider_; - - AutocompleteMatchType::Type type_; - - // The relevance score. - int relevance_; - - private: - // Whether this result's relevance score was fully or partly calculated - // based on server information, and thus is assumed to be more accurate. - // This is ultimately used in - // SearchProvider::ConvertResultsToAutocompleteMatches(), see comments - // there. - bool relevance_from_server_; - - // Optional deletion URL provided with suggestions. Fetching this URL - // should result in some reasonable deletion behaviour on the server, - // e.g. deleting this term out of a user's server-side search history. - std::string deletion_url_; - }; - - class SuggestResult : public Result { - public: - 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); - virtual ~SuggestResult(); - - const base::string16& suggestion() const { return suggestion_; } - const base::string16& match_contents_prefix() const { - return match_contents_prefix_; - } - const base::string16& annotation() const { return annotation_; } - const std::string& suggest_query_params() const { - return suggest_query_params_; - } - - const base::string16& answer_contents() const { return answer_contents_; } - const base::string16& answer_type() const { return answer_type_; } - - bool should_prefetch() const { return should_prefetch_; } - - // Fills in |match_contents_class_| to reflect how |match_contents_| should - // be displayed and bolded against the current |input_text|. If - // |allow_bolding_all| is false and |match_contents_class_| would have all - // of |match_contents_| bolded, do nothing. - void ClassifyMatchContents(const bool allow_bolding_all, - const base::string16& input_text); - - // Result: - virtual int CalculateRelevance( - const AutocompleteInput& input, - bool keyword_provider_requested) const OVERRIDE; - - private: - // The search terms to be used for this suggestion. - base::string16 suggestion_; - - // The contents to be displayed as prefix of match contents. - // Used for postfix suggestions to display a leading ellipsis (or some - // equivalent character) to indicate omitted text. - // Only used to pass this information to about:omnibox's "Additional Info". - base::string16 match_contents_prefix_; - - // Optional annotation for the |match_contents_| for disambiguation. - // This may be displayed in the autocomplete match contents, but is defined - // separately to facilitate different formatting. - base::string16 annotation_; - - // Optional additional parameters to be added to the search URL. - std::string suggest_query_params_; - - // Optional formatted Answers result. - base::string16 answer_contents_; - - // Type of optional formatted Answers result. - base::string16 answer_type_; - - // Should this result be prefetched? - bool should_prefetch_; - }; - - class NavigationResult : public Result { - public: - 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); - virtual ~NavigationResult(); - - const GURL& url() const { return url_; } - const base::string16& description() const { return description_; } - const base::string16& formatted_url() const { return formatted_url_; } - - // Fills in |match_contents_| and |match_contents_class_| to reflect how - // the URL should be displayed and bolded against the current |input_text| - // and user |languages|. If |allow_bolding_nothing| is false and - // |match_contents_class_| would result in an entirely unbolded - // |match_contents_|, do nothing. - void CalculateAndClassifyMatchContents(const bool allow_bolding_nothing, - const base::string16& input_text, - const std::string& languages); - - // Result: - virtual int CalculateRelevance( - const AutocompleteInput& input, - bool keyword_provider_requested) const OVERRIDE; - - private: - // The suggested url for navigation. - GURL url_; - - // The properly formatted ("fixed up") URL string with equivalent meaning - // to the one in |url_|. - base::string16 formatted_url_; - - // The suggested navigational result description; generally the site name. - base::string16 description_; - }; - - typedef std::vector<SuggestResult> SuggestResults; - typedef std::vector<NavigationResult> NavigationResults; typedef std::pair<base::string16, std::string> MatchKey; typedef std::map<MatchKey, AutocompleteMatch> MatchMap; typedef ScopedVector<SuggestionDeletionHandler> SuggestionDeletionHandlers; - // A simple structure bundling most of the information (including - // both SuggestResults and NavigationResults) returned by a call to - // the suggest server. - // - // This has to be declared after the typedefs since it relies on some of them. - struct Results { - Results(); - ~Results(); - - // Clears |suggest_results| and |navigation_results| and resets - // |verbatim_relevance| to -1 (implies unset). - void Clear(); - - // Returns whether any of the results (including verbatim) have - // server-provided scores. - bool HasServerProvidedScores() const; - - // Query suggestions sorted by relevance score. - SuggestResults suggest_results; - - // Navigational suggestions sorted by relevance score. - NavigationResults navigation_results; - - // The server supplied verbatim relevance scores. Negative values - // indicate that there is no suggested score; a value of 0 - // suppresses the verbatim result. - int verbatim_relevance; - - // The JSON metadata associated with this server response. - std::string metadata; - - private: - DISALLOW_COPY_AND_ASSIGN(Results); - }; - // Returns an AutocompleteMatch with the given |autocomplete_provider| // for the search |suggestion|, which represents a search via |template_url|. // If |template_url| is NULL, returns a match with an invalid destination URL. @@ -341,7 +121,7 @@ class BaseSearchProvider : public AutocompleteProvider, static AutocompleteMatch CreateSearchSuggestion( AutocompleteProvider* autocomplete_provider, const AutocompleteInput& input, - const SuggestResult& suggestion, + const SearchSuggestionParser::SuggestResult& suggestion, const TemplateURL* template_url, const SearchTermsData& search_terms_data, int accepted_suggestion, @@ -408,7 +188,7 @@ class BaseSearchProvider : public AutocompleteProvider, // AutocompleteMatch. // |mark_as_deletable| indicates whether the match should be marked deletable. // NOTE: Any result containing a deletion URL is always marked deletable. - void AddMatchToMap(const SuggestResult& result, + void AddMatchToMap(const SearchSuggestionParser::SuggestResult& result, const std::string& metadata, int accepted_suggestion, bool mark_as_deletable, @@ -420,15 +200,12 @@ class BaseSearchProvider : public AutocompleteProvider, // Returns whether the appropriate result list members were updated. bool ParseSuggestResults(const base::Value& root_val, bool is_keyword_result, - Results* results); - - // Prefetches any images in Answers results. - void PrefetchAnswersImages(const base::DictionaryValue* answers_json); + SearchSuggestionParser::Results* results); // Called at the end of ParseSuggestResults to rank the |results|. virtual void SortResults(bool is_keyword, - const base::ListValue* relevances, - Results* results); + bool relevances_from_server, + SearchSuggestionParser::Results* results); // Optionally, cache the received |json_data| and return true if we want // to stop processing results at this point. The |parsed_data| is the parsed @@ -445,11 +222,13 @@ class BaseSearchProvider : public AutocompleteProvider, virtual const AutocompleteInput GetInput(bool is_keyword) const = 0; // Returns a pointer to a Results object, which will hold suggest results. - virtual Results* GetResultsToFill(bool is_keyword) = 0; + virtual SearchSuggestionParser::Results* GetResultsToFill( + bool is_keyword) = 0; // Returns whether the destination URL corresponding to the given |result| // should contain command-line-specified query params. - virtual bool ShouldAppendExtraParams(const SuggestResult& result) const = 0; + virtual bool ShouldAppendExtraParams( + const SearchSuggestionParser::SuggestResult& result) const = 0; // Stops the suggest query. // NOTE: This does not update |done_|. Callers must do so. diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc index 0e492ba..37de50a 100644 --- a/chrome/browser/autocomplete/search_provider.cc +++ b/chrome/browser/autocomplete/search_provider.cc @@ -119,7 +119,8 @@ const TemplateURL* SearchProvider::Providers::GetKeywordProviderURL() const { class SearchProvider::CompareScoredResults { public: - bool operator()(const Result& a, const Result& b) { + bool operator()(const SearchSuggestionParser::Result& a, + const SearchSuggestionParser::Result& b) { // Sort in descending relevance order. return a.relevance() > b.relevance(); } @@ -149,15 +150,18 @@ void SearchProvider::ResetSession() { SearchProvider::~SearchProvider() { } -void SearchProvider::UpdateMatchContentsClass(const base::string16& input_text, - Results* results) { - for (SuggestResults::iterator sug_it = results->suggest_results.begin(); +void SearchProvider::UpdateMatchContentsClass( + const base::string16& input_text, + SearchSuggestionParser::Results* results) { + for (SearchSuggestionParser::SuggestResults::iterator sug_it = + results->suggest_results.begin(); sug_it != results->suggest_results.end(); ++sug_it) { sug_it->ClassifyMatchContents(false, input_text); } const std::string languages( profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); - for (NavigationResults::iterator nav_it = results->navigation_results.begin(); + for (SearchSuggestionParser::NavigationResults::iterator nav_it = + results->navigation_results.begin(); nav_it != results->navigation_results.end(); ++nav_it) { nav_it->CalculateAndClassifyMatchContents(false, input_text, languages); } @@ -263,16 +267,16 @@ void SearchProvider::Start(const AutocompleteInput& input, } void SearchProvider::SortResults(bool is_keyword, - const base::ListValue* relevances, - Results* results) { + bool relevances_from_server, + SearchSuggestionParser::Results* results) { // Ignore suggested scores for non-keyword matches in keyword mode; if the // server is allowed to score these, it could interfere with the user's // ability to get good keyword results. const bool abandon_suggested_scores = !is_keyword && !providers_.keyword_provider().empty(); - // Apply calculated relevance scores to suggestions if a valid list was + // Apply calculated relevance scores to suggestions if valid relevances were // not provided or we're abandoning suggested scores entirely. - if ((relevances == NULL) || abandon_suggested_scores) { + if (!relevances_from_server || abandon_suggested_scores) { ApplyCalculatedSuggestRelevance(&results->suggest_results); ApplyCalculatedNavigationRelevance(&results->navigation_results); // If abandoning scores entirely, also abandon the verbatim score. @@ -299,12 +303,13 @@ const AutocompleteInput SearchProvider::GetInput(bool is_keyword) const { return is_keyword ? keyword_input_ : input_; } -BaseSearchProvider::Results* SearchProvider::GetResultsToFill(bool is_keyword) { +SearchSuggestionParser::Results* SearchProvider::GetResultsToFill( + bool is_keyword) { return is_keyword ? &keyword_results_ : &default_results_; } bool SearchProvider::ShouldAppendExtraParams( - const SuggestResult& result) const { + const SearchSuggestionParser::SuggestResult& result) const { return !result.from_keyword_provider() || providers_.default_provider().empty(); } @@ -612,9 +617,10 @@ void SearchProvider::ApplyCalculatedRelevance() { keyword_results_.verbatim_relevance = -1; } -void SearchProvider::ApplyCalculatedSuggestRelevance(SuggestResults* list) { +void SearchProvider::ApplyCalculatedSuggestRelevance( + SearchSuggestionParser::SuggestResults* list) { for (size_t i = 0; i < list->size(); ++i) { - SuggestResult& result = (*list)[i]; + SearchSuggestionParser::SuggestResult& result = (*list)[i]; result.set_relevance( result.CalculateRelevance(input_, providers_.has_keyword_provider()) + (list->size() - i - 1)); @@ -623,9 +629,9 @@ void SearchProvider::ApplyCalculatedSuggestRelevance(SuggestResults* list) { } void SearchProvider::ApplyCalculatedNavigationRelevance( - NavigationResults* list) { + SearchSuggestionParser::NavigationResults* list) { for (size_t i = 0; i < list->size(); ++i) { - NavigationResult& result = (*list)[i]; + SearchSuggestionParser::NavigationResult& result = (*list)[i]; result.set_relevance( result.CalculateRelevance(input_, providers_.has_keyword_provider()) + (list->size() - i - 1)); @@ -707,7 +713,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { if (verbatim_relevance > 0) { const base::string16& trimmed_verbatim = base::CollapseWhitespace(input_.text(), false); - SuggestResult verbatim( + SearchSuggestionParser::SuggestResult verbatim( trimmed_verbatim, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, trimmed_verbatim, base::string16(), base::string16(), base::string16(), base::string16(), std::string(), std::string(), false, @@ -732,7 +738,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { if (keyword_verbatim_relevance > 0) { const base::string16& trimmed_verbatim = base::CollapseWhitespace(keyword_input_.text(), false); - SuggestResult verbatim( + SearchSuggestionParser::SuggestResult verbatim( trimmed_verbatim, AutocompleteMatchType::SEARCH_OTHER_ENGINE, trimmed_verbatim, base::string16(), base::string16(), base::string16(), base::string16(), std::string(), std::string(), @@ -832,10 +838,10 @@ bool SearchProvider::IsTopMatchSearchWithURLInput() const { } void SearchProvider::AddNavigationResultsToMatches( - const NavigationResults& navigation_results, + const SearchSuggestionParser::NavigationResults& navigation_results, ACMatches* matches) { - for (NavigationResults::const_iterator it = navigation_results.begin(); - it != navigation_results.end(); ++it) { + for (SearchSuggestionParser::NavigationResults::const_iterator it = + navigation_results.begin(); it != navigation_results.end(); ++it) { matches->push_back(NavigationToMatch(*it)); // In the absence of suggested relevance scores, use only the single // highest-scoring result. (The results are already sorted by relevance.) @@ -858,7 +864,7 @@ void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, is_keyword ? keyword_input_.text() : input_.text(); bool input_multiple_words = HasMultipleWords(input_text); - SuggestResults scored_results; + SearchSuggestionParser::SuggestResults scored_results; if (!prevent_inline_autocomplete && input_multiple_words) { // ScoreHistoryResults() allows autocompletion of multi-word, 1-visit // queries if the input also has multiple words. But if we were already @@ -879,21 +885,21 @@ void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, scored_results = ScoreHistoryResults(results, prevent_inline_autocomplete, input_multiple_words, input_text, is_keyword); - for (SuggestResults::const_iterator i(scored_results.begin()); - i != scored_results.end(); ++i) { + for (SearchSuggestionParser::SuggestResults::const_iterator i( + scored_results.begin()); i != scored_results.end(); ++i) { AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true, map); } UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime", base::TimeTicks::Now() - start_time); } -SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( +SearchSuggestionParser::SuggestResults SearchProvider::ScoreHistoryResults( const HistoryResults& results, bool base_prevent_inline_autocomplete, bool input_multiple_words, const base::string16& input_text, bool is_keyword) { - SuggestResults scored_results; + SearchSuggestionParser::SuggestResults scored_results; // True if the user has asked this exact query previously. bool found_what_you_typed_match = false; const bool prevent_search_history_inlining = @@ -918,16 +924,19 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( // Add the match to |scored_results| by putting the what-you-typed match // on the front and appending all other matches. We want the what-you- // typed match to always be first. - SuggestResults::iterator insertion_position = scored_results.end(); + SearchSuggestionParser::SuggestResults::iterator insertion_position = + scored_results.end(); if (trimmed_suggestion == trimmed_input) { found_what_you_typed_match = true; insertion_position = scored_results.begin(); } - scored_results.insert(insertion_position, SuggestResult( - trimmed_suggestion, AutocompleteMatchType::SEARCH_HISTORY, - trimmed_suggestion, base::string16(), base::string16(), - base::string16(), base::string16(), std::string(), std::string(), - is_keyword, relevance, false, false, trimmed_input)); + scored_results.insert( + insertion_position, + SearchSuggestionParser::SuggestResult( + trimmed_suggestion, AutocompleteMatchType::SEARCH_HISTORY, + trimmed_suggestion, base::string16(), base::string16(), + base::string16(), base::string16(), std::string(), std::string(), + is_keyword, relevance, false, false, trimmed_input)); } // History returns results sorted for us. However, we may have docked some @@ -986,8 +995,8 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( } } - for (SuggestResults::iterator i(scored_results.begin()); - i != scored_results.end(); ++i) { + for (SearchSuggestionParser::SuggestResults::iterator i( + scored_results.begin()); i != scored_results.end(); ++i) { if ((last_relevance != 0) && (i->relevance() >= last_relevance)) i->set_relevance(last_relevance - 1); last_relevance = i->relevance(); @@ -996,9 +1005,10 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( return scored_results; } -void SearchProvider::AddSuggestResultsToMap(const SuggestResults& results, - const std::string& metadata, - MatchMap* map) { +void SearchProvider::AddSuggestResultsToMap( + const SearchSuggestionParser::SuggestResults& results, + const std::string& metadata, + MatchMap* map) { for (size_t i = 0; i < results.size(); ++i) AddMatchToMap(results[i], metadata, i, false, map); } @@ -1112,7 +1122,7 @@ int SearchProvider::CalculateRelevanceForHistory( } AutocompleteMatch SearchProvider::NavigationToMatch( - const NavigationResult& navigation) { + const SearchSuggestionParser::NavigationResult& navigation) { base::string16 input; const bool trimmed_whitespace = base::TrimWhitespace( navigation.from_keyword_provider() ? diff --git a/chrome/browser/autocomplete/search_provider.h b/chrome/browser/autocomplete/search_provider.h index a0e47d4..cfd2e20 100644 --- a/chrome/browser/autocomplete/search_provider.h +++ b/chrome/browser/autocomplete/search_provider.h @@ -140,15 +140,16 @@ class SearchProvider : public BaseSearchProvider { // Removes non-inlineable results until either the top result can inline // autocomplete the current input or verbatim outscores the top result. - static void RemoveStaleResults(const base::string16& input, - int verbatim_relevance, - SuggestResults* suggest_results, - NavigationResults* navigation_results); + static void RemoveStaleResults( + const base::string16& input, + int verbatim_relevance, + SearchSuggestionParser::SuggestResults* suggest_results, + SearchSuggestionParser::NavigationResults* navigation_results); // Recalculates the match contents class of |results| to better display // against the current input and user's language. void UpdateMatchContentsClass(const base::string16& input_text, - Results* results); + SearchSuggestionParser::Results* results); // Calculates the relevance score for the keyword verbatim result (if the // input matches one of the profile's keyword). @@ -162,13 +163,14 @@ class SearchProvider : public BaseSearchProvider { // BaseSearchProvider: virtual void SortResults(bool is_keyword, - const base::ListValue* relevances, - Results* results) OVERRIDE; + bool relevances_from_server, + SearchSuggestionParser::Results* results) OVERRIDE; virtual const TemplateURL* GetTemplateURL(bool is_keyword) const OVERRIDE; virtual const AutocompleteInput GetInput(bool is_keyword) const OVERRIDE; - virtual Results* GetResultsToFill(bool is_keyword) OVERRIDE; + virtual SearchSuggestionParser::Results* GetResultsToFill( + bool is_keyword) OVERRIDE; virtual bool ShouldAppendExtraParams( - const SuggestResult& result) const OVERRIDE; + const SearchSuggestionParser::SuggestResult& result) const OVERRIDE; virtual void StopSuggest() OVERRIDE; virtual void ClearAllResults() OVERRIDE; virtual int GetDefaultResultRelevance() const OVERRIDE; @@ -200,8 +202,10 @@ class SearchProvider : public BaseSearchProvider { // Apply calculated relevance scores to the current results. void ApplyCalculatedRelevance(); - void ApplyCalculatedSuggestRelevance(SuggestResults* list); - void ApplyCalculatedNavigationRelevance(NavigationResults* list); + void ApplyCalculatedSuggestRelevance( + SearchSuggestionParser::SuggestResults* list); + void ApplyCalculatedNavigationRelevance( + SearchSuggestionParser::NavigationResults* list); // Starts a new URLFetcher requesting suggest results from |template_url|; // callers own the returned URLFetcher, which is NULL for invalid providers. @@ -224,7 +228,7 @@ class SearchProvider : public BaseSearchProvider { // Converts an appropriate number of navigation results in // |navigation_results| to matches and adds them to |matches|. void AddNavigationResultsToMatches( - const NavigationResults& navigation_results, + const SearchSuggestionParser::NavigationResults& navigation_results, ACMatches* matches); // Adds a match for each result in |results| to |map|. |is_keyword| indicates @@ -235,16 +239,18 @@ class SearchProvider : public BaseSearchProvider { MatchMap* map); // Calculates relevance scores for all |results|. - SuggestResults ScoreHistoryResults(const HistoryResults& results, - bool base_prevent_inline_autocomplete, - bool input_multiple_words, - const base::string16& input_text, - bool is_keyword); + SearchSuggestionParser::SuggestResults ScoreHistoryResults( + const HistoryResults& results, + bool base_prevent_inline_autocomplete, + bool input_multiple_words, + const base::string16& input_text, + bool is_keyword); // Adds matches for |results| to |map|. - void AddSuggestResultsToMap(const SuggestResults& results, - const std::string& metadata, - MatchMap* map); + void AddSuggestResultsToMap( + const SearchSuggestionParser::SuggestResults& results, + const std::string& metadata, + MatchMap* map); // Gets the relevance score for the verbatim result. This value may be // provided by the suggest server or calculated locally; if @@ -283,7 +289,8 @@ class SearchProvider : public BaseSearchProvider { bool prevent_search_history_inlining) const; // Returns an AutocompleteMatch for a navigational suggestion. - AutocompleteMatch NavigationToMatch(const NavigationResult& navigation); + AutocompleteMatch NavigationToMatch( + const SearchSuggestionParser::NavigationResult& navigation); // Updates the value of |done_| from the internal state. void UpdateDone(); @@ -324,8 +331,8 @@ class SearchProvider : public BaseSearchProvider { scoped_ptr<net::URLFetcher> default_fetcher_; // Results from the default and keyword search providers. - Results default_results_; - Results keyword_results_; + SearchSuggestionParser::Results default_results_; + SearchSuggestionParser::Results keyword_results_; GURL current_page_url_; diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc index b17ca1f..d92fa2b 100644 --- a/chrome/browser/autocomplete/search_provider_unittest.cc +++ b/chrome/browser/autocomplete/search_provider_unittest.cc @@ -2376,7 +2376,7 @@ TEST_F(SearchProviderTest, NavigationInline) { // First test regular mode. QueryForInput(ASCIIToUTF16(cases[i].input), false, false); AutocompleteMatch match( - provider_->NavigationToMatch(SearchProvider::NavigationResult( + provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult( ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url), AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), false, 0, false, ASCIIToUTF16(cases[i].input), std::string()))); @@ -2389,7 +2389,7 @@ TEST_F(SearchProviderTest, NavigationInline) { // Then test prevent-inline-autocomplete mode. QueryForInput(ASCIIToUTF16(cases[i].input), true, false); AutocompleteMatch match_prevent_inline( - provider_->NavigationToMatch(SearchProvider::NavigationResult( + provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult( ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url), AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), false, 0, false, ASCIIToUTF16(cases[i].input), std::string()))); @@ -2406,7 +2406,7 @@ TEST_F(SearchProviderTest, NavigationInline) { TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { const base::string16 input(ASCIIToUTF16("ht")); const base::string16 url(ASCIIToUTF16("http://a.com")); - const SearchProvider::NavigationResult result( + const SearchSuggestionParser::NavigationResult result( ChromeAutocompleteSchemeClassifier(&profile_), GURL(url), AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), false, 0, false, input, std::string()); @@ -2431,7 +2431,7 @@ TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) { TEST_F(SearchProviderTest, NavigationInlineDomainClassify) { QueryForInput(ASCIIToUTF16("w"), false, false); AutocompleteMatch match( - provider_->NavigationToMatch(SearchProvider::NavigationResult( + provider_->NavigationToMatch(SearchSuggestionParser::NavigationResult( ChromeAutocompleteSchemeClassifier(&profile_), GURL("http://www.wow.com"), AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(), diff --git a/chrome/browser/autocomplete/zero_suggest_provider.cc b/chrome/browser/autocomplete/zero_suggest_provider.cc index 83060f3..b36ccf4 100644 --- a/chrome/browser/autocomplete/zero_suggest_provider.cc +++ b/chrome/browser/autocomplete/zero_suggest_provider.cc @@ -212,14 +212,14 @@ const AutocompleteInput ZeroSuggestProvider::GetInput(bool is_keyword) const { true, ChromeAutocompleteSchemeClassifier(profile_)); } -BaseSearchProvider::Results* ZeroSuggestProvider::GetResultsToFill( +SearchSuggestionParser::Results* ZeroSuggestProvider::GetResultsToFill( bool is_keyword) { DCHECK(!is_keyword); return &results_; } bool ZeroSuggestProvider::ShouldAppendExtraParams( - const SuggestResult& result) const { + const SearchSuggestionParser::SuggestResult& result) const { // We always use the default provider for search, so append the params. return true; } @@ -272,14 +272,14 @@ void ZeroSuggestProvider::UpdateMatches() { } void ZeroSuggestProvider::AddSuggestResultsToMap( - const SuggestResults& results, + const SearchSuggestionParser::SuggestResults& results, MatchMap* map) { for (size_t i = 0; i < results.size(); ++i) AddMatchToMap(results[i], std::string(), i, false, map); } AutocompleteMatch ZeroSuggestProvider::NavigationToMatch( - const NavigationResult& navigation) { + const SearchSuggestionParser::NavigationResult& navigation) { AutocompleteMatch match(this, navigation.relevance(), false, navigation.type()); match.destination_url = navigation.url(); @@ -376,7 +376,7 @@ void ZeroSuggestProvider::ConvertResultsToAutocompleteMatches() { profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); for (size_t i = 0; i < most_visited_urls_.size(); i++) { const history::MostVisitedURL& url = most_visited_urls_[i]; - NavigationResult nav( + SearchSuggestionParser::NavigationResult nav( ChromeAutocompleteSchemeClassifier(profile_), url.url, AutocompleteMatchType::NAVSUGGEST, url.title, std::string(), false, relevance, true, current_query_string16, languages); @@ -396,9 +396,10 @@ void ZeroSuggestProvider::ConvertResultsToAutocompleteMatches() { for (MatchMap::const_iterator it(map.begin()); it != map.end(); ++it) matches_.push_back(it->second); - const NavigationResults& nav_results(results_.navigation_results); - for (NavigationResults::const_iterator it(nav_results.begin()); - it != nav_results.end(); ++it) + const SearchSuggestionParser::NavigationResults& nav_results( + results_.navigation_results); + for (SearchSuggestionParser::NavigationResults::const_iterator it( + nav_results.begin()); it != nav_results.end(); ++it) matches_.push_back(NavigationToMatch(*it)); } diff --git a/chrome/browser/autocomplete/zero_suggest_provider.h b/chrome/browser/autocomplete/zero_suggest_provider.h index f9abe2d..6ad0b00 100644 --- a/chrome/browser/autocomplete/zero_suggest_provider.h +++ b/chrome/browser/autocomplete/zero_suggest_provider.h @@ -80,9 +80,10 @@ class ZeroSuggestProvider : public BaseSearchProvider { const base::Value& parsed_data) OVERRIDE; virtual const TemplateURL* GetTemplateURL(bool is_keyword) const OVERRIDE; virtual const AutocompleteInput GetInput(bool is_keyword) const OVERRIDE; - virtual Results* GetResultsToFill(bool is_keyword) OVERRIDE; + virtual SearchSuggestionParser::Results* GetResultsToFill( + bool is_keyword) OVERRIDE; virtual bool ShouldAppendExtraParams( - const SuggestResult& result) const OVERRIDE; + const SearchSuggestionParser::SuggestResult& result) const OVERRIDE; virtual void StopSuggest() OVERRIDE; virtual void ClearAllResults() OVERRIDE; virtual int GetDefaultResultRelevance() const OVERRIDE; @@ -93,10 +94,13 @@ class ZeroSuggestProvider : public BaseSearchProvider { // Adds AutocompleteMatches for each of the suggestions in |results| to // |map|. - void AddSuggestResultsToMap(const SuggestResults& results, MatchMap* map); + void AddSuggestResultsToMap( + const SearchSuggestionParser::SuggestResults& results, + MatchMap* map); // Returns an AutocompleteMatch for a navigational suggestion |navigation|. - AutocompleteMatch NavigationToMatch(const NavigationResult& navigation); + AutocompleteMatch NavigationToMatch( + const SearchSuggestionParser::NavigationResult& navigation); // Fetches zero-suggest suggestions by sending a request using |suggest_url|. void Run(const GURL& suggest_url); @@ -149,7 +153,7 @@ class ZeroSuggestProvider : public BaseSearchProvider { // Contains suggest and navigation results as well as relevance parsed from // the response for the most recent zero suggest input URL. - Results results_; + SearchSuggestionParser::Results results_; // Whether we are currently showing cached zero suggest results. bool results_from_cache_; diff --git a/components/autocomplete.gypi b/components/autocomplete.gypi index 1f823f58..2b3a590 100644 --- a/components/autocomplete.gypi +++ b/components/autocomplete.gypi @@ -31,6 +31,8 @@ 'autocomplete/autocomplete_provider.cc', 'autocomplete/autocomplete_provider.h', 'autocomplete/autocomplete_scheme_classifier.h', + 'autocomplete/search_suggestion_parser.cc', + 'autocomplete/search_suggestion_parser.h', 'autocomplete/url_prefix.cc', 'autocomplete/url_prefix.h', ], diff --git a/components/autocomplete/search_suggestion_parser.cc b/components/autocomplete/search_suggestion_parser.cc new file mode 100644 index 0000000..5ce7443 --- /dev/null +++ b/components/autocomplete/search_suggestion_parser.cc @@ -0,0 +1,457 @@ +// 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/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" + +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) {} + +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 +bool SearchSuggestionParser::ParseSuggestResults( + const base::Value& root_val, + const AutocompleteInput& input, + const AutocompleteSchemeClassifier& scheme_classifier, + const ImagePrefetchCallback& image_prefetch_callback, + int default_result_relevance, + const std::string& languages, + bool is_keyword_result, + bool* relevances_from_server, + 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(); + + 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; + if (!image_prefetch_callback.is_null()) + PrefetchAnswersImages(answer_json, image_prefetch_callback); + 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)); + } + } + *relevances_from_server = relevances != NULL; + return true; +} + +// static +void SearchSuggestionParser::PrefetchAnswersImages( + const base::DictionaryValue* answer_json, + const ImagePrefetchCallback& image_prefetch_callback) { + DCHECK(answer_json); + DCHECK(!image_prefetch_callback.is_null()); + 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); + image_prefetch_callback.Run(GURL(imageUrl)); + } +} diff --git a/components/autocomplete/search_suggestion_parser.h b/components/autocomplete/search_suggestion_parser.h new file mode 100644 index 0000000..ca2ac8f --- /dev/null +++ b/components/autocomplete/search_suggestion_parser.h @@ -0,0 +1,279 @@ +// 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. + +#ifndef COMPONENTS_AUTOCOMPLETE_SEARCH_SUGGESTION_PARSER_H_ +#define COMPONENTS_AUTOCOMPLETE_SEARCH_SUGGESTION_PARSER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/strings/string16.h" +#include "components/autocomplete/autocomplete_match.h" +#include "components/autocomplete/autocomplete_match_type.h" +#include "url/gurl.h" + +class AutocompleteInput; +class AutocompleteSchemeClassifier; + +namespace base { +class DictionaryValue; +class Value; +} + +class SearchSuggestionParser { + public: + // The Result classes are intermediate representations of AutocompleteMatches, + // simply containing relevance-ranked search and navigation suggestions. + // They may be cached to provide some synchronous matches while requests for + // new suggestions from updated input are in flight. + // TODO(msw) Extend these classes to generate their corresponding matches and + // other requisite data, in order to consolidate and simplify the + // highly fragmented SearchProvider logic for each Result type. + class Result { + public: + Result(bool from_keyword_provider, + int relevance, + bool relevance_from_server, + AutocompleteMatchType::Type type, + const std::string& deletion_url); + virtual ~Result(); + + bool from_keyword_provider() const { return from_keyword_provider_; } + + const base::string16& match_contents() const { return match_contents_; } + const ACMatchClassifications& match_contents_class() const { + return match_contents_class_; + } + + AutocompleteMatchType::Type type() const { return type_; } + int relevance() const { return relevance_; } + void set_relevance(int relevance) { relevance_ = relevance; } + + bool relevance_from_server() const { return relevance_from_server_; } + void set_relevance_from_server(bool relevance_from_server) { + relevance_from_server_ = relevance_from_server; + } + + const std::string& deletion_url() const { return deletion_url_; } + + // Returns the default relevance value for this result (which may + // be left over from a previous omnibox input) given the current + // input and whether the current input caused a keyword provider + // to be active. + virtual int CalculateRelevance(const AutocompleteInput& input, + bool keyword_provider_requested) const = 0; + + protected: + // The contents to be displayed and its style info. + base::string16 match_contents_; + ACMatchClassifications match_contents_class_; + + // True if the result came from the keyword provider. + bool from_keyword_provider_; + + AutocompleteMatchType::Type type_; + + // The relevance score. + int relevance_; + + private: + // Whether this result's relevance score was fully or partly calculated + // based on server information, and thus is assumed to be more accurate. + // This is ultimately used in + // SearchProvider::ConvertResultsToAutocompleteMatches(), see comments + // there. + bool relevance_from_server_; + + // Optional deletion URL provided with suggestions. Fetching this URL + // should result in some reasonable deletion behaviour on the server, + // e.g. deleting this term out of a user's server-side search history. + std::string deletion_url_; + }; + + class SuggestResult : public Result { + public: + 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); + virtual ~SuggestResult(); + + const base::string16& suggestion() const { return suggestion_; } + const base::string16& match_contents_prefix() const { + return match_contents_prefix_; + } + const base::string16& annotation() const { return annotation_; } + const std::string& suggest_query_params() const { + return suggest_query_params_; + } + + const base::string16& answer_contents() const { return answer_contents_; } + const base::string16& answer_type() const { return answer_type_; } + + bool should_prefetch() const { return should_prefetch_; } + + // Fills in |match_contents_class_| to reflect how |match_contents_| should + // be displayed and bolded against the current |input_text|. If + // |allow_bolding_all| is false and |match_contents_class_| would have all + // of |match_contents_| bolded, do nothing. + void ClassifyMatchContents(const bool allow_bolding_all, + const base::string16& input_text); + + // Result: + virtual int CalculateRelevance( + const AutocompleteInput& input, + bool keyword_provider_requested) const OVERRIDE; + + private: + // The search terms to be used for this suggestion. + base::string16 suggestion_; + + // The contents to be displayed as prefix of match contents. + // Used for postfix suggestions to display a leading ellipsis (or some + // equivalent character) to indicate omitted text. + // Only used to pass this information to about:omnibox's "Additional Info". + base::string16 match_contents_prefix_; + + // Optional annotation for the |match_contents_| for disambiguation. + // This may be displayed in the autocomplete match contents, but is defined + // separately to facilitate different formatting. + base::string16 annotation_; + + // Optional additional parameters to be added to the search URL. + std::string suggest_query_params_; + + // Optional formatted Answers result. + base::string16 answer_contents_; + + // Type of optional formatted Answers result. + base::string16 answer_type_; + + // Should this result be prefetched? + bool should_prefetch_; + }; + + class NavigationResult : public Result { + public: + 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); + virtual ~NavigationResult(); + + const GURL& url() const { return url_; } + const base::string16& description() const { return description_; } + const base::string16& formatted_url() const { return formatted_url_; } + + // Fills in |match_contents_| and |match_contents_class_| to reflect how + // the URL should be displayed and bolded against the current |input_text| + // and user |languages|. If |allow_bolding_nothing| is false and + // |match_contents_class_| would result in an entirely unbolded + // |match_contents_|, do nothing. + void CalculateAndClassifyMatchContents(const bool allow_bolding_nothing, + const base::string16& input_text, + const std::string& languages); + + // Result: + virtual int CalculateRelevance( + const AutocompleteInput& input, + bool keyword_provider_requested) const OVERRIDE; + + private: + // The suggested url for navigation. + GURL url_; + + // The properly formatted ("fixed up") URL string with equivalent meaning + // to the one in |url_|. + base::string16 formatted_url_; + + // The suggested navigational result description; generally the site name. + base::string16 description_; + }; + + typedef std::vector<SuggestResult> SuggestResults; + typedef std::vector<NavigationResult> NavigationResults; + + // A simple structure bundling most of the information (including + // both SuggestResults and NavigationResults) returned by a call to + // the suggest server. + // + // This has to be declared after the typedefs since it relies on some of them. + struct Results { + Results(); + ~Results(); + + // Clears |suggest_results| and |navigation_results| and resets + // |verbatim_relevance| to -1 (implies unset). + void Clear(); + + // Returns whether any of the results (including verbatim) have + // server-provided scores. + bool HasServerProvidedScores() const; + + // Query suggestions sorted by relevance score. + SuggestResults suggest_results; + + // Navigational suggestions sorted by relevance score. + NavigationResults navigation_results; + + // The server supplied verbatim relevance scores. Negative values + // indicate that there is no suggested score; a value of 0 + // suppresses the verbatim result. + int verbatim_relevance; + + // The JSON metadata associated with this server response. + std::string metadata; + + // If the active suggest field trial (if any) has triggered. + bool field_trial_triggered; + + private: + DISALLOW_COPY_AND_ASSIGN(Results); + }; + + typedef base::Callback<void(const GURL& image_url)> ImagePrefetchCallback; + + // Parses results from the suggest server and updates the appropriate suggest + // and navigation result lists in |results|. |is_keyword_result| indicates + // whether the response was received from the keyword provider. + // Returns whether the appropriate result list members were updated. + static bool ParseSuggestResults( + const base::Value& root_val, + const AutocompleteInput& input, + const AutocompleteSchemeClassifier& scheme_classifier, + const ImagePrefetchCallback& image_prefetch_callback, + int default_result_relevance, + const std::string& languages, + bool is_keyword_result, + bool* relevances_from_server, + Results* results); + + private: + // Prefetches any images in Answers results. + static void PrefetchAnswersImages( + const base::DictionaryValue* answer_json, + const ImagePrefetchCallback& image_prefetch_callback); + + DISALLOW_COPY_AND_ASSIGN(SearchSuggestionParser); +}; + +#endif // COMPONENTS_AUTOCOMPLETE_SEARCH_SUGGESTION_PARSER_H_ |