summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-30 11:58:37 +0000
committerhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-30 11:58:37 +0000
commit0b9575f6161a2e382bbc2c77484a01fe8a4b73f3 (patch)
treeb0ee63027ed74980ef54bfb53f588f4dd0d7100d
parent3cb7c2b55cfa24ae40cd49a1d8fd15275b55dd0b (diff)
downloadchromium_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.cc470
-rw-r--r--chrome/browser/autocomplete/base_search_provider.h241
-rw-r--r--chrome/browser/autocomplete/search_provider.cc84
-rw-r--r--chrome/browser/autocomplete/search_provider.h53
-rw-r--r--chrome/browser/autocomplete/search_provider_unittest.cc8
-rw-r--r--chrome/browser/autocomplete/zero_suggest_provider.cc17
-rw-r--r--chrome/browser/autocomplete/zero_suggest_provider.h14
-rw-r--r--components/autocomplete.gypi2
-rw-r--r--components/autocomplete/search_suggestion_parser.cc457
-rw-r--r--components/autocomplete/search_suggestion_parser.h279
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_