Index: chrome/browser/autocomplete/search_provider.cc |
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc |
deleted file mode 100644 |
index 90172527f321754687cf3324791051f2668f0fa3..0000000000000000000000000000000000000000 |
--- a/chrome/browser/autocomplete/search_provider.cc |
+++ /dev/null |
@@ -1,1256 +0,0 @@ |
-// Copyright 2012 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 "chrome/browser/autocomplete/search_provider.h" |
- |
-#include <algorithm> |
-#include <cmath> |
- |
-#include "base/base64.h" |
-#include "base/callback.h" |
-#include "base/i18n/break_iterator.h" |
-#include "base/i18n/case_conversion.h" |
-#include "base/json/json_string_value_serializer.h" |
-#include "base/metrics/histogram.h" |
-#include "base/metrics/user_metrics.h" |
-#include "base/rand_util.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "components/history/core/browser/in_memory_database.h" |
-#include "components/history/core/browser/keyword_search_term.h" |
-#include "components/metrics/proto/omnibox_input_type.pb.h" |
-#include "components/omnibox/autocomplete_provider_delegate.h" |
-#include "components/omnibox/autocomplete_provider_listener.h" |
-#include "components/omnibox/autocomplete_result.h" |
-#include "components/omnibox/keyword_provider.h" |
-#include "components/omnibox/omnibox_field_trial.h" |
-#include "components/omnibox/url_prefix.h" |
-#include "components/search/search.h" |
-#include "components/search_engines/template_url_prepopulate_data.h" |
-#include "components/search_engines/template_url_service.h" |
-#include "components/variations/variations_http_header_provider.h" |
-#include "grit/components_strings.h" |
-#include "net/base/escape.h" |
-#include "net/base/load_flags.h" |
-#include "net/base/net_util.h" |
-#include "net/http/http_request_headers.h" |
-#include "net/url_request/url_fetcher.h" |
-#include "net/url_request/url_request_status.h" |
-#include "ui/base/l10n/l10n_util.h" |
-#include "url/url_constants.h" |
-#include "url/url_util.h" |
- |
-// Helpers -------------------------------------------------------------------- |
- |
-namespace { |
- |
-// We keep track in a histogram how many suggest requests we send, how |
-// many suggest requests we invalidate (e.g., due to a user typing |
-// another character), and how many replies we receive. |
-// *** ADD NEW ENUMS AFTER ALL PREVIOUSLY DEFINED ONES! *** |
-// (excluding the end-of-list enum value) |
-// We do not want values of existing enums to change or else it screws |
-// up the statistics. |
-enum SuggestRequestsHistogramValue { |
- REQUEST_SENT = 1, |
- REQUEST_INVALIDATED, |
- REPLY_RECEIVED, |
- MAX_SUGGEST_REQUEST_HISTOGRAM_VALUE |
-}; |
- |
-// The verbatim score for an input which is not an URL. |
-const int kNonURLVerbatimRelevance = 1300; |
- |
-// Increments the appropriate value in the histogram by one. |
-void LogOmniboxSuggestRequest( |
- SuggestRequestsHistogramValue request_value) { |
- UMA_HISTOGRAM_ENUMERATION("Omnibox.SuggestRequests", request_value, |
- MAX_SUGGEST_REQUEST_HISTOGRAM_VALUE); |
-} |
- |
-bool HasMultipleWords(const base::string16& text) { |
- base::i18n::BreakIterator i(text, base::i18n::BreakIterator::BREAK_WORD); |
- bool found_word = false; |
- if (i.Init()) { |
- while (i.Advance()) { |
- if (i.IsWord()) { |
- if (found_word) |
- return true; |
- found_word = true; |
- } |
- } |
- } |
- return false; |
-} |
- |
-} // namespace |
- |
-// SearchProvider::Providers -------------------------------------------------- |
- |
-SearchProvider::Providers::Providers(TemplateURLService* template_url_service) |
- : template_url_service_(template_url_service) {} |
- |
-const TemplateURL* SearchProvider::Providers::GetDefaultProviderURL() const { |
- return default_provider_.empty() ? NULL : |
- template_url_service_->GetTemplateURLForKeyword(default_provider_); |
-} |
- |
-const TemplateURL* SearchProvider::Providers::GetKeywordProviderURL() const { |
- return keyword_provider_.empty() ? NULL : |
- template_url_service_->GetTemplateURLForKeyword(keyword_provider_); |
-} |
- |
- |
-// SearchProvider::CompareScoredResults --------------------------------------- |
- |
-class SearchProvider::CompareScoredResults { |
- public: |
- bool operator()(const SearchSuggestionParser::Result& a, |
- const SearchSuggestionParser::Result& b) { |
- // Sort in descending relevance order. |
- return a.relevance() > b.relevance(); |
- } |
-}; |
- |
- |
-// SearchProvider ------------------------------------------------------------- |
- |
-// static |
-int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100; |
- |
-SearchProvider::SearchProvider( |
- AutocompleteProviderListener* listener, |
- TemplateURLService* template_url_service, |
- scoped_ptr<AutocompleteProviderDelegate> delegate) |
- : BaseSearchProvider(template_url_service, delegate.Pass(), |
- AutocompleteProvider::TYPE_SEARCH), |
- listener_(listener), |
- suggest_results_pending_(0), |
- providers_(template_url_service), |
- answers_cache_(1) { |
-} |
- |
-// static |
-std::string SearchProvider::GetSuggestMetadata(const AutocompleteMatch& match) { |
- return match.GetAdditionalInfo(kSuggestMetadataKey); |
-} |
- |
-void SearchProvider::ResetSession() { |
- field_trial_triggered_in_session_ = false; |
-} |
- |
-SearchProvider::~SearchProvider() { |
-} |
- |
-// static |
-int SearchProvider::CalculateRelevanceForKeywordVerbatim( |
- metrics::OmniboxInputType::Type type, |
- bool prefer_keyword) { |
- // This function is responsible for scoring verbatim query matches |
- // for non-extension keywords. KeywordProvider::CalculateRelevance() |
- // scores verbatim query matches for extension keywords, as well as |
- // for keyword matches (i.e., suggestions of a keyword itself, not a |
- // suggestion of a query on a keyword search engine). These two |
- // functions are currently in sync, but there's no reason we |
- // couldn't decide in the future to score verbatim matches |
- // differently for extension and non-extension keywords. If you |
- // make such a change, however, you should update this comment to |
- // describe it, so it's clear why the functions diverge. |
- if (prefer_keyword) |
- return 1500; |
- return (type == metrics::OmniboxInputType::QUERY) ? 1450 : 1100; |
-} |
- |
-void SearchProvider::Start(const AutocompleteInput& input, |
- bool minimal_changes) { |
- // Do our best to load the model as early as possible. This will reduce |
- // odds of having the model not ready when really needed (a non-empty input). |
- TemplateURLService* model = providers_.template_url_service(); |
- DCHECK(model); |
- model->Load(); |
- |
- matches_.clear(); |
- field_trial_triggered_ = false; |
- |
- // Can't return search/suggest results for bogus input. |
- if (input.type() == metrics::OmniboxInputType::INVALID) { |
- Stop(true); |
- return; |
- } |
- |
- keyword_input_ = input; |
- const TemplateURL* keyword_provider = |
- KeywordProvider::GetSubstitutingTemplateURLForInput(model, |
- &keyword_input_); |
- if (keyword_provider == NULL) |
- keyword_input_.Clear(); |
- else if (keyword_input_.text().empty()) |
- keyword_provider = NULL; |
- |
- const TemplateURL* default_provider = model->GetDefaultSearchProvider(); |
- if (default_provider && |
- !default_provider->SupportsReplacement(model->search_terms_data())) |
- default_provider = NULL; |
- |
- if (keyword_provider == default_provider) |
- default_provider = NULL; // No use in querying the same provider twice. |
- |
- if (!default_provider && !keyword_provider) { |
- // No valid providers. |
- Stop(true); |
- return; |
- } |
- |
- // If we're still running an old query but have since changed the query text |
- // or the providers, abort the query. |
- base::string16 default_provider_keyword(default_provider ? |
- default_provider->keyword() : base::string16()); |
- base::string16 keyword_provider_keyword(keyword_provider ? |
- keyword_provider->keyword() : base::string16()); |
- if (!minimal_changes || |
- !providers_.equal(default_provider_keyword, keyword_provider_keyword)) { |
- // Cancel any in-flight suggest requests. |
- if (!done_) |
- Stop(false); |
- } |
- |
- providers_.set(default_provider_keyword, keyword_provider_keyword); |
- |
- if (input.text().empty()) { |
- // User typed "?" alone. Give them a placeholder result indicating what |
- // this syntax does. |
- if (default_provider) { |
- AutocompleteMatch match; |
- match.provider = this; |
- match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)); |
- match.contents_class.push_back( |
- ACMatchClassification(0, ACMatchClassification::NONE)); |
- match.keyword = providers_.default_provider(); |
- match.allowed_to_be_default_match = true; |
- matches_.push_back(match); |
- } |
- Stop(true); |
- return; |
- } |
- |
- input_ = input; |
- |
- DoHistoryQuery(minimal_changes); |
- DoAnswersQuery(input); |
- StartOrStopSuggestQuery(minimal_changes); |
- UpdateMatches(); |
-} |
- |
-void SearchProvider::Stop(bool clear_cached_results) { |
- StopSuggest(); |
- done_ = true; |
- |
- if (clear_cached_results) |
- ClearAllResults(); |
-} |
- |
-const TemplateURL* SearchProvider::GetTemplateURL(bool is_keyword) const { |
- return is_keyword ? providers_.GetKeywordProviderURL() |
- : providers_.GetDefaultProviderURL(); |
-} |
- |
-const AutocompleteInput SearchProvider::GetInput(bool is_keyword) const { |
- return is_keyword ? keyword_input_ : input_; |
-} |
- |
-bool SearchProvider::ShouldAppendExtraParams( |
- const SearchSuggestionParser::SuggestResult& result) const { |
- return !result.from_keyword_provider() || |
- providers_.default_provider().empty(); |
-} |
- |
-void SearchProvider::RecordDeletionResult(bool success) { |
- if (success) { |
- base::RecordAction( |
- base::UserMetricsAction("Omnibox.ServerSuggestDelete.Success")); |
- } else { |
- base::RecordAction( |
- base::UserMetricsAction("Omnibox.ServerSuggestDelete.Failure")); |
- } |
-} |
- |
-void SearchProvider::OnURLFetchComplete(const net::URLFetcher* source) { |
- DCHECK(!done_); |
- --suggest_results_pending_; |
- DCHECK_GE(suggest_results_pending_, 0); // Should never go negative. |
- |
- const bool is_keyword = source == keyword_fetcher_.get(); |
- |
- // Ensure the request succeeded and that the provider used is still available. |
- // A verbatim match cannot be generated without this provider, causing errors. |
- const bool request_succeeded = |
- source->GetStatus().is_success() && (source->GetResponseCode() == 200) && |
- GetTemplateURL(is_keyword); |
- |
- LogFetchComplete(request_succeeded, is_keyword); |
- |
- bool results_updated = false; |
- if (request_succeeded) { |
- scoped_ptr<base::Value> data(SearchSuggestionParser::DeserializeJsonData( |
- SearchSuggestionParser::ExtractJsonData(source))); |
- if (data) { |
- SearchSuggestionParser::Results* results = |
- is_keyword ? &keyword_results_ : &default_results_; |
- results_updated = ParseSuggestResults(*data, -1, is_keyword, results); |
- if (results_updated) |
- SortResults(is_keyword, results); |
- } |
- } |
- UpdateMatches(); |
- if (done_ || results_updated) |
- listener_->OnProviderUpdate(results_updated); |
-} |
- |
-void SearchProvider::StopSuggest() { |
- // Increment the appropriate field in the histogram by the number of |
- // pending requests that were invalidated. |
- for (int i = 0; i < suggest_results_pending_; ++i) |
- LogOmniboxSuggestRequest(REQUEST_INVALIDATED); |
- suggest_results_pending_ = 0; |
- timer_.Stop(); |
- // Stop any in-progress URL fetches. |
- keyword_fetcher_.reset(); |
- default_fetcher_.reset(); |
-} |
- |
-void SearchProvider::ClearAllResults() { |
- keyword_results_.Clear(); |
- default_results_.Clear(); |
-} |
- |
-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(delegate_->AcceptLanguages()); |
- 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); |
- } |
-} |
- |
-void SearchProvider::SortResults(bool is_keyword, |
- 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 valid relevances were |
- // not provided or we're abandoning suggested scores entirely. |
- if (!results->relevances_from_server || abandon_suggested_scores) { |
- ApplyCalculatedSuggestRelevance(&results->suggest_results); |
- ApplyCalculatedNavigationRelevance(&results->navigation_results); |
- // If abandoning scores entirely, also abandon the verbatim score. |
- if (abandon_suggested_scores) |
- results->verbatim_relevance = -1; |
- } |
- |
- // Keep the result lists sorted. |
- const CompareScoredResults comparator = CompareScoredResults(); |
- std::stable_sort(results->suggest_results.begin(), |
- results->suggest_results.end(), |
- comparator); |
- std::stable_sort(results->navigation_results.begin(), |
- results->navigation_results.end(), |
- comparator); |
-} |
- |
-void SearchProvider::LogFetchComplete(bool success, bool is_keyword) { |
- LogOmniboxSuggestRequest(REPLY_RECEIVED); |
- // Record response time for suggest requests sent to Google. We care |
- // only about the common case: the Google default provider used in |
- // non-keyword mode. |
- const TemplateURL* default_url = providers_.GetDefaultProviderURL(); |
- if (!is_keyword && default_url && |
- (TemplateURLPrepopulateData::GetEngineType( |
- *default_url, |
- providers_.template_url_service()->search_terms_data()) == |
- SEARCH_ENGINE_GOOGLE)) { |
- const base::TimeDelta elapsed_time = |
- base::TimeTicks::Now() - time_suggest_request_sent_; |
- if (success) { |
- UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Success.GoogleResponseTime", |
- elapsed_time); |
- } else { |
- UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Failure.GoogleResponseTime", |
- elapsed_time); |
- } |
- } |
-} |
- |
-void SearchProvider::UpdateMatches() { |
- ConvertResultsToAutocompleteMatches(); |
- |
- // Check constraints that may be violated by suggested relevances. |
- if (!matches_.empty() && |
- (default_results_.HasServerProvidedScores() || |
- keyword_results_.HasServerProvidedScores())) { |
- // These blocks attempt to repair undesirable behavior by suggested |
- // relevances with minimal impact, preserving other suggested relevances. |
- |
- const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
- const bool is_extension_keyword = (keyword_url != NULL) && |
- (keyword_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION); |
- if ((keyword_url != NULL) && !is_extension_keyword && |
- (FindTopMatch() == matches_.end())) { |
- // In non-extension keyword mode, disregard the keyword verbatim suggested |
- // relevance if necessary, so at least one match is allowed to be default. |
- // (In extension keyword mode this is not necessary because the extension |
- // will return a default match.) |
- keyword_results_.verbatim_relevance = -1; |
- ConvertResultsToAutocompleteMatches(); |
- } |
- if (IsTopMatchSearchWithURLInput()) { |
- // Disregard the suggested search and verbatim relevances if the input |
- // type is URL and the top match is a highly-ranked search suggestion. |
- // For example, prevent a search for "foo.com" from outranking another |
- // provider's navigation for "foo.com" or "foo.com/url_from_history". |
- ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results); |
- ApplyCalculatedSuggestRelevance(&default_results_.suggest_results); |
- default_results_.verbatim_relevance = -1; |
- keyword_results_.verbatim_relevance = -1; |
- ConvertResultsToAutocompleteMatches(); |
- } |
- if (!is_extension_keyword && (FindTopMatch() == matches_.end())) { |
- // Guarantee that SearchProvider returns a legal default match (except |
- // when in extension-based keyword mode). The omnibox always needs at |
- // least one legal default match, and it relies on SearchProvider in |
- // combination with KeywordProvider (for extension-based keywords) to |
- // always return one. |
- ApplyCalculatedRelevance(); |
- ConvertResultsToAutocompleteMatches(); |
- } |
- DCHECK(!IsTopMatchSearchWithURLInput()); |
- DCHECK(is_extension_keyword || (FindTopMatch() != matches_.end())); |
- } |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- "Omnibox.SearchProviderMatches", matches_.size(), 1, 6, 7); |
- UpdateDone(); |
-} |
- |
-void SearchProvider::Run() { |
- // Start a new request with the current input. |
- suggest_results_pending_ = 0; |
- time_suggest_request_sent_ = base::TimeTicks::Now(); |
- |
- default_fetcher_.reset(CreateSuggestFetcher(kDefaultProviderURLFetcherID, |
- providers_.GetDefaultProviderURL(), input_)); |
- keyword_fetcher_.reset(CreateSuggestFetcher(kKeywordProviderURLFetcherID, |
- providers_.GetKeywordProviderURL(), keyword_input_)); |
- |
- // Both the above can fail if the providers have been modified or deleted |
- // since the query began. |
- if (suggest_results_pending_ == 0) { |
- UpdateDone(); |
- // We only need to update the listener if we're actually done. |
- if (done_) |
- listener_->OnProviderUpdate(false); |
- } |
-} |
- |
-void SearchProvider::DoHistoryQuery(bool minimal_changes) { |
- // The history query results are synchronous, so if minimal_changes is true, |
- // we still have the last results and don't need to do anything. |
- if (minimal_changes) |
- return; |
- |
- keyword_history_results_.clear(); |
- default_history_results_.clear(); |
- |
- if (OmniboxFieldTrial::SearchHistoryDisable( |
- input_.current_page_classification())) |
- return; |
- |
- history::URLDatabase* url_db = delegate_->InMemoryDatabase(); |
- if (!url_db) |
- return; |
- |
- // Request history for both the keyword and default provider. We grab many |
- // more matches than we'll ultimately clamp to so that if there are several |
- // recent multi-word matches who scores are lowered (see |
- // AddHistoryResultsToMap()), they won't crowd out older, higher-scoring |
- // matches. Note that this doesn't fix the problem entirely, but merely |
- // limits it to cases with a very large number of such multi-word matches; for |
- // now, this seems OK compared with the complexity of a real fix, which would |
- // require multiple searches and tracking of "single- vs. multi-word" in the |
- // database. |
- int num_matches = kMaxMatches * 5; |
- const TemplateURL* default_url = providers_.GetDefaultProviderURL(); |
- if (default_url) { |
- const base::TimeTicks start_time = base::TimeTicks::Now(); |
- url_db->GetMostRecentKeywordSearchTerms(default_url->id(), input_.text(), |
- num_matches, &default_history_results_); |
- UMA_HISTOGRAM_TIMES( |
- "Omnibox.SearchProvider.GetMostRecentKeywordTermsDefaultProviderTime", |
- base::TimeTicks::Now() - start_time); |
- } |
- const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
- if (keyword_url) { |
- url_db->GetMostRecentKeywordSearchTerms(keyword_url->id(), |
- keyword_input_.text(), num_matches, &keyword_history_results_); |
- } |
-} |
- |
-void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) { |
- if (!IsQuerySuitableForSuggest()) { |
- StopSuggest(); |
- ClearAllResults(); |
- return; |
- } |
- |
- // For the minimal_changes case, if we finished the previous query and still |
- // have its results, or are allowed to keep running it, just do that, rather |
- // than starting a new query. |
- if (minimal_changes && |
- (!default_results_.suggest_results.empty() || |
- !default_results_.navigation_results.empty() || |
- !keyword_results_.suggest_results.empty() || |
- !keyword_results_.navigation_results.empty() || |
- (!done_ && input_.want_asynchronous_matches()))) |
- return; |
- |
- // We can't keep running any previous query, so halt it. |
- StopSuggest(); |
- |
- // Remove existing results that cannot inline autocomplete the new input. |
- RemoveAllStaleResults(); |
- |
- // Update the content classifications of remaining results so they look good |
- // against the current input. |
- UpdateMatchContentsClass(input_.text(), &default_results_); |
- if (!keyword_input_.text().empty()) |
- UpdateMatchContentsClass(keyword_input_.text(), &keyword_results_); |
- |
- // We can't start a new query if we're only allowed synchronous results. |
- if (!input_.want_asynchronous_matches()) |
- return; |
- |
- // To avoid flooding the suggest server, don't send a query until at |
- // least 100 ms since the last query. |
- base::TimeTicks next_suggest_time(time_suggest_request_sent_ + |
- base::TimeDelta::FromMilliseconds(kMinimumTimeBetweenSuggestQueriesMs)); |
- base::TimeTicks now(base::TimeTicks::Now()); |
- if (now >= next_suggest_time) { |
- Run(); |
- return; |
- } |
- timer_.Start(FROM_HERE, next_suggest_time - now, this, &SearchProvider::Run); |
-} |
- |
-bool SearchProvider::IsQuerySuitableForSuggest() const { |
- // Don't run Suggest in incognito mode, if the engine doesn't support it, or |
- // if the user has disabled it. |
- const TemplateURL* default_url = providers_.GetDefaultProviderURL(); |
- const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
- if (delegate_->IsOffTheRecord() || |
- ((!default_url || default_url->suggestions_url().empty()) && |
- (!keyword_url || keyword_url->suggestions_url().empty())) || |
- !delegate_->SearchSuggestEnabled()) |
- return false; |
- |
- // If the input type might be a URL, we take extra care so that private data |
- // isn't sent to the server. |
- |
- // FORCED_QUERY means the user is explicitly asking us to search for this, so |
- // we assume it isn't a URL and/or there isn't private data. |
- if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) |
- return true; |
- |
- // Next we check the scheme. If this is UNKNOWN/URL with a scheme that isn't |
- // http/https/ftp, we shouldn't send it. Sending things like file: and data: |
- // is both a waste of time and a disclosure of potentially private, local |
- // data. Other "schemes" may actually be usernames, and we don't want to send |
- // passwords. If the scheme is OK, we still need to check other cases below. |
- // If this is QUERY, then the presence of these schemes means the user |
- // explicitly typed one, and thus this is probably a URL that's being entered |
- // and happens to currently be invalid -- in which case we again want to run |
- // our checks below. Other QUERY cases are less likely to be URLs and thus we |
- // assume we're OK. |
- if (!LowerCaseEqualsASCII(input_.scheme(), url::kHttpScheme) && |
- !LowerCaseEqualsASCII(input_.scheme(), url::kHttpsScheme) && |
- !LowerCaseEqualsASCII(input_.scheme(), url::kFtpScheme)) |
- return (input_.type() == metrics::OmniboxInputType::QUERY); |
- |
- // Don't send URLs with usernames, queries or refs. Some of these are |
- // private, and the Suggest server is unlikely to have any useful results |
- // for any of them. Also don't send URLs with ports, as we may initially |
- // think that a username + password is a host + port (and we don't want to |
- // send usernames/passwords), and even if the port really is a port, the |
- // server is once again unlikely to have and useful results. |
- // Note that we only block based on refs if the input is URL-typed, as search |
- // queries can legitimately have #s in them which the URL parser |
- // overaggressively categorizes as a url with a ref. |
- const url::Parsed& parts = input_.parts(); |
- if (parts.username.is_nonempty() || parts.port.is_nonempty() || |
- parts.query.is_nonempty() || |
- (parts.ref.is_nonempty() && |
- (input_.type() == metrics::OmniboxInputType::URL))) |
- return false; |
- |
- // Don't send anything for https except the hostname. Hostnames are OK |
- // because they are visible when the TCP connection is established, but the |
- // specific path may reveal private information. |
- if (LowerCaseEqualsASCII(input_.scheme(), url::kHttpsScheme) && |
- parts.path.is_nonempty()) |
- return false; |
- |
- return true; |
-} |
- |
-void SearchProvider::RemoveAllStaleResults() { |
- if (keyword_input_.text().empty()) { |
- // User is either in keyword mode with a blank input or out of |
- // keyword mode entirely. |
- keyword_results_.Clear(); |
- } |
-} |
- |
-void SearchProvider::ApplyCalculatedRelevance() { |
- ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results); |
- ApplyCalculatedSuggestRelevance(&default_results_.suggest_results); |
- ApplyCalculatedNavigationRelevance(&keyword_results_.navigation_results); |
- ApplyCalculatedNavigationRelevance(&default_results_.navigation_results); |
- default_results_.verbatim_relevance = -1; |
- keyword_results_.verbatim_relevance = -1; |
-} |
- |
-void SearchProvider::ApplyCalculatedSuggestRelevance( |
- SearchSuggestionParser::SuggestResults* list) { |
- for (size_t i = 0; i < list->size(); ++i) { |
- SearchSuggestionParser::SuggestResult& result = (*list)[i]; |
- result.set_relevance( |
- result.CalculateRelevance(input_, providers_.has_keyword_provider()) + |
- (list->size() - i - 1)); |
- result.set_relevance_from_server(false); |
- } |
-} |
- |
-void SearchProvider::ApplyCalculatedNavigationRelevance( |
- SearchSuggestionParser::NavigationResults* list) { |
- for (size_t i = 0; i < list->size(); ++i) { |
- SearchSuggestionParser::NavigationResult& result = (*list)[i]; |
- result.set_relevance( |
- result.CalculateRelevance(input_, providers_.has_keyword_provider()) + |
- (list->size() - i - 1)); |
- result.set_relevance_from_server(false); |
- } |
-} |
- |
-net::URLFetcher* SearchProvider::CreateSuggestFetcher( |
- int id, |
- const TemplateURL* template_url, |
- const AutocompleteInput& input) { |
- if (!template_url || template_url->suggestions_url().empty()) |
- return NULL; |
- |
- // Bail if the suggestion URL is invalid with the given replacements. |
- TemplateURLRef::SearchTermsArgs search_term_args(input.text()); |
- search_term_args.input_type = input.type(); |
- search_term_args.cursor_position = input.cursor_position(); |
- search_term_args.page_classification = input.current_page_classification(); |
- if (OmniboxFieldTrial::EnableAnswersInSuggest()) { |
- search_term_args.session_token = GetSessionToken(); |
- if (!prefetch_data_.full_query_text.empty()) { |
- search_term_args.prefetch_query = |
- base::UTF16ToUTF8(prefetch_data_.full_query_text); |
- search_term_args.prefetch_query_type = |
- base::UTF16ToUTF8(prefetch_data_.query_type); |
- } |
- } |
- GURL suggest_url(template_url->suggestions_url_ref().ReplaceSearchTerms( |
- search_term_args, |
- providers_.template_url_service()->search_terms_data())); |
- if (!suggest_url.is_valid()) |
- return NULL; |
- // Send the current page URL if user setting and URL requirements are met and |
- // the user is in the field trial. |
- if (CanSendURL(current_page_url_, suggest_url, template_url, |
- input.current_page_classification(), |
- template_url_service_->search_terms_data(), delegate_.get()) && |
- OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()) { |
- search_term_args.current_page_url = current_page_url_.spec(); |
- // Create the suggest URL again with the current page URL. |
- suggest_url = GURL(template_url->suggestions_url_ref().ReplaceSearchTerms( |
- search_term_args, |
- providers_.template_url_service()->search_terms_data())); |
- } |
- |
- suggest_results_pending_++; |
- LogOmniboxSuggestRequest(REQUEST_SENT); |
- |
- net::URLFetcher* fetcher = |
- net::URLFetcher::Create(id, suggest_url, net::URLFetcher::GET, this); |
- fetcher->SetRequestContext(delegate_->RequestContext()); |
- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); |
- // Add Chrome experiment state to the request headers. |
- net::HttpRequestHeaders headers; |
- variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( |
- fetcher->GetOriginalURL(), delegate_->IsOffTheRecord(), false, &headers); |
- fetcher->SetExtraRequestHeaders(headers.ToString()); |
- fetcher->Start(); |
- return fetcher; |
-} |
- |
-void SearchProvider::ConvertResultsToAutocompleteMatches() { |
- // Convert all the results to matches and add them to a map, so we can keep |
- // the most relevant match for each result. |
- base::TimeTicks start_time(base::TimeTicks::Now()); |
- MatchMap map; |
- const base::Time no_time; |
- int did_not_accept_keyword_suggestion = |
- keyword_results_.suggest_results.empty() ? |
- TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : |
- TemplateURLRef::NO_SUGGESTION_CHOSEN; |
- |
- bool relevance_from_server; |
- int verbatim_relevance = GetVerbatimRelevance(&relevance_from_server); |
- int did_not_accept_default_suggestion = |
- default_results_.suggest_results.empty() ? |
- TemplateURLRef::NO_SUGGESTIONS_AVAILABLE : |
- TemplateURLRef::NO_SUGGESTION_CHOSEN; |
- const TemplateURL* keyword_url = providers_.GetKeywordProviderURL(); |
- if (verbatim_relevance > 0) { |
- const base::string16& trimmed_verbatim = |
- base::CollapseWhitespace(input_.text(), false); |
- |
- // Verbatim results don't get suggestions and hence, answers. |
- // Scan previous matches if the last answer-bearing suggestion matches |
- // verbatim, and if so, copy over answer contents. |
- base::string16 answer_contents; |
- base::string16 answer_type; |
- for (ACMatches::iterator it = matches_.begin(); it != matches_.end(); |
- ++it) { |
- if (!it->answer_contents.empty() && |
- it->fill_into_edit == trimmed_verbatim) { |
- answer_contents = it->answer_contents; |
- answer_type = it->answer_type; |
- break; |
- } |
- } |
- |
- SearchSuggestionParser::SuggestResult verbatim( |
- trimmed_verbatim, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, |
- trimmed_verbatim, base::string16(), base::string16(), answer_contents, |
- answer_type, std::string(), std::string(), false, verbatim_relevance, |
- relevance_from_server, false, trimmed_verbatim); |
- AddMatchToMap(verbatim, std::string(), did_not_accept_default_suggestion, |
- false, keyword_url != NULL, &map); |
- } |
- if (!keyword_input_.text().empty()) { |
- // We only create the verbatim search query match for a keyword |
- // if it's not an extension keyword. Extension keywords are handled |
- // in KeywordProvider::Start(). (Extensions are complicated...) |
- // Note: in this provider, SEARCH_OTHER_ENGINE must correspond |
- // to the keyword verbatim search query. Do not create other matches |
- // of type SEARCH_OTHER_ENGINE. |
- if (keyword_url && |
- (keyword_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION)) { |
- bool keyword_relevance_from_server; |
- const int keyword_verbatim_relevance = |
- GetKeywordVerbatimRelevance(&keyword_relevance_from_server); |
- if (keyword_verbatim_relevance > 0) { |
- const base::string16& trimmed_verbatim = |
- base::CollapseWhitespace(keyword_input_.text(), false); |
- SearchSuggestionParser::SuggestResult verbatim( |
- trimmed_verbatim, AutocompleteMatchType::SEARCH_OTHER_ENGINE, |
- trimmed_verbatim, base::string16(), base::string16(), |
- base::string16(), base::string16(), std::string(), std::string(), |
- true, keyword_verbatim_relevance, keyword_relevance_from_server, |
- false, trimmed_verbatim); |
- AddMatchToMap(verbatim, std::string(), |
- did_not_accept_keyword_suggestion, false, true, &map); |
- } |
- } |
- } |
- AddHistoryResultsToMap(keyword_history_results_, true, |
- did_not_accept_keyword_suggestion, &map); |
- AddHistoryResultsToMap(default_history_results_, false, |
- did_not_accept_default_suggestion, &map); |
- |
- AddSuggestResultsToMap(keyword_results_.suggest_results, |
- keyword_results_.metadata, &map); |
- AddSuggestResultsToMap(default_results_.suggest_results, |
- default_results_.metadata, &map); |
- |
- ACMatches matches; |
- for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i) |
- matches.push_back(i->second); |
- |
- AddNavigationResultsToMatches(keyword_results_.navigation_results, &matches); |
- AddNavigationResultsToMatches(default_results_.navigation_results, &matches); |
- |
- // Now add the most relevant matches to |matches_|. We take up to kMaxMatches |
- // suggest/navsuggest matches, regardless of origin. If Instant Extended is |
- // enabled and we have server-provided (and thus hopefully more accurate) |
- // scores for some suggestions, we allow more of those, until we reach |
- // AutocompleteResult::kMaxMatches total matches (that is, enough to fill the |
- // whole popup). |
- // |
- // We will always return any verbatim matches, no matter how we obtained their |
- // scores, unless we have already accepted AutocompleteResult::kMaxMatches |
- // higher-scoring matches under the conditions above. |
- std::sort(matches.begin(), matches.end(), &AutocompleteMatch::MoreRelevant); |
- matches_.clear(); |
- |
- size_t num_suggestions = 0; |
- for (ACMatches::const_iterator i(matches.begin()); |
- (i != matches.end()) && |
- (matches_.size() < AutocompleteResult::kMaxMatches); |
- ++i) { |
- // SEARCH_OTHER_ENGINE is only used in the SearchProvider for the keyword |
- // verbatim result, so this condition basically means "if this match is a |
- // suggestion of some sort". |
- if ((i->type != AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED) && |
- (i->type != AutocompleteMatchType::SEARCH_OTHER_ENGINE)) { |
- // If we've already hit the limit on non-server-scored suggestions, and |
- // this isn't a server-scored suggestion we can add, skip it. |
- if ((num_suggestions >= kMaxMatches) && |
- (!chrome::IsInstantExtendedAPIEnabled() || |
- (i->GetAdditionalInfo(kRelevanceFromServerKey) != kTrue))) { |
- continue; |
- } |
- |
- ++num_suggestions; |
- } |
- |
- matches_.push_back(*i); |
- } |
- UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.ConvertResultsTime", |
- base::TimeTicks::Now() - start_time); |
-} |
- |
-ACMatches::const_iterator SearchProvider::FindTopMatch() const { |
- ACMatches::const_iterator it = matches_.begin(); |
- while ((it != matches_.end()) && !it->allowed_to_be_default_match) |
- ++it; |
- return it; |
-} |
- |
-bool SearchProvider::IsTopMatchSearchWithURLInput() const { |
- ACMatches::const_iterator first_match = FindTopMatch(); |
- return (input_.type() == metrics::OmniboxInputType::URL) && |
- (first_match != matches_.end()) && |
- (first_match->relevance > CalculateRelevanceForVerbatim()) && |
- (first_match->type != AutocompleteMatchType::NAVSUGGEST) && |
- (first_match->type != AutocompleteMatchType::NAVSUGGEST_PERSONALIZED); |
-} |
- |
-void SearchProvider::AddNavigationResultsToMatches( |
- const SearchSuggestionParser::NavigationResults& navigation_results, |
- ACMatches* matches) { |
- 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.) |
- if (!it->relevance_from_server()) |
- return; |
- } |
-} |
- |
-void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, |
- bool is_keyword, |
- int did_not_accept_suggestion, |
- MatchMap* map) { |
- if (results.empty()) |
- return; |
- |
- base::TimeTicks start_time(base::TimeTicks::Now()); |
- bool prevent_inline_autocomplete = input_.prevent_inline_autocomplete() || |
- (input_.type() == metrics::OmniboxInputType::URL); |
- const base::string16& input_text = |
- is_keyword ? keyword_input_.text() : input_.text(); |
- bool input_multiple_words = HasMultipleWords(input_text); |
- |
- 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 |
- // scoring a multi-word, multi-visit query aggressively, and the current |
- // input is still a prefix of it, then changing the suggestion suddenly |
- // feels wrong. To detect this case, first score as if only one word has |
- // been typed, then check if the best result came from aggressive search |
- // history scoring. If it did, then just keep that score set. This |
- // 1200 the lowest possible score in CalculateRelevanceForHistory()'s |
- // aggressive-scoring curve. |
- scored_results = ScoreHistoryResults(results, prevent_inline_autocomplete, |
- false, input_text, is_keyword); |
- if ((scored_results.front().relevance() < 1200) || |
- !HasMultipleWords(scored_results.front().suggestion())) |
- scored_results.clear(); // Didn't detect the case above, score normally. |
- } |
- if (scored_results.empty()) |
- scored_results = ScoreHistoryResults(results, prevent_inline_autocomplete, |
- input_multiple_words, input_text, |
- is_keyword); |
- for (SearchSuggestionParser::SuggestResults::const_iterator i( |
- scored_results.begin()); i != scored_results.end(); ++i) { |
- AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true, |
- providers_.GetKeywordProviderURL() != NULL, map); |
- } |
- UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime", |
- base::TimeTicks::Now() - start_time); |
-} |
- |
-SearchSuggestionParser::SuggestResults SearchProvider::ScoreHistoryResults( |
- const HistoryResults& results, |
- bool base_prevent_inline_autocomplete, |
- bool input_multiple_words, |
- const base::string16& input_text, |
- bool is_keyword) { |
- 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 = |
- OmniboxFieldTrial::SearchHistoryPreventInlining( |
- input_.current_page_classification()); |
- const base::string16& trimmed_input = |
- base::CollapseWhitespace(input_text, false); |
- for (HistoryResults::const_iterator i(results.begin()); i != results.end(); |
- ++i) { |
- const base::string16& trimmed_suggestion = |
- base::CollapseWhitespace(i->term, false); |
- |
- // Don't autocomplete multi-word queries that have only been seen once |
- // unless the user has typed more than one word. |
- bool prevent_inline_autocomplete = base_prevent_inline_autocomplete || |
- (!input_multiple_words && (i->visits < 2) && |
- HasMultipleWords(trimmed_suggestion)); |
- |
- int relevance = CalculateRelevanceForHistory( |
- i->time, is_keyword, !prevent_inline_autocomplete, |
- prevent_search_history_inlining); |
- // 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. |
- 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, |
- 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 |
- // results' scores, so things are no longer in order. While keeping the |
- // what-you-typed match at the front (if it exists), do a stable sort to get |
- // things back in order without otherwise disturbing results with equal |
- // scores, then force the scores to be unique, so that the order in which |
- // they're shown is deterministic. |
- std::stable_sort(scored_results.begin() + |
- (found_what_you_typed_match ? 1 : 0), |
- scored_results.end(), |
- CompareScoredResults()); |
- |
- // Don't autocomplete to search terms that would normally be treated as URLs |
- // when typed. For example, if the user searched for "google.com" and types |
- // "goog", don't autocomplete to the search term "google.com". Otherwise, |
- // the input will look like a URL but act like a search, which is confusing. |
- // The 1200 relevance score threshold in the test below is the lowest |
- // possible score in CalculateRelevanceForHistory()'s aggressive-scoring |
- // curve. This is an appropriate threshold to use to decide if we're overly |
- // aggressively inlining because, if we decide the answer is yes, the |
- // way we resolve it it to not use the aggressive-scoring curve. |
- // NOTE: We don't check for autocompleting to URLs in the following cases: |
- // * When inline autocomplete is disabled, we won't be inline autocompleting |
- // this term, so we don't need to worry about confusion as much. This |
- // also prevents calling Classify() again from inside the classifier |
- // (which will corrupt state and likely crash), since the classifier |
- // always disables inline autocomplete. |
- // * When the user has typed the whole string before as a query, then it's |
- // likely the user has no expectation that term should be interpreted as |
- // as a URL, so we need not do anything special to preserve user |
- // expectation. |
- int last_relevance = 0; |
- if (!base_prevent_inline_autocomplete && !found_what_you_typed_match && |
- scored_results.front().relevance() >= 1200) { |
- AutocompleteMatch match; |
- delegate_->Classify(scored_results.front().suggestion(), false, false, |
- input_.current_page_classification(), &match, NULL); |
- // Demote this match that would normally be interpreted as a URL to have |
- // the highest score a previously-issued search query could have when |
- // scoring with the non-aggressive method. A consequence of demoting |
- // by revising |last_relevance| is that this match and all following |
- // matches get demoted; the relative order of matches is preserved. |
- // One could imagine demoting only those matches that might cause |
- // confusion (which, by the way, might change the relative order of |
- // matches. We have decided to go with the simple demote-all approach |
- // because selective demotion requires multiple Classify() calls and |
- // such calls can be expensive (as expensive as running the whole |
- // autocomplete system). |
- if (!AutocompleteMatch::IsSearchType(match.type)) { |
- last_relevance = CalculateRelevanceForHistory( |
- base::Time::Now(), is_keyword, false, |
- prevent_search_history_inlining); |
- } |
- } |
- |
- 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(); |
- } |
- |
- return scored_results; |
-} |
- |
-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, |
- providers_.GetKeywordProviderURL() != NULL, map); |
- } |
-} |
- |
-int SearchProvider::GetVerbatimRelevance(bool* relevance_from_server) const { |
- // Use the suggested verbatim relevance score if it is non-negative (valid), |
- // if inline autocomplete isn't prevented (always show verbatim on backspace), |
- // and if it won't suppress verbatim, leaving no default provider matches. |
- // Otherwise, if the default provider returned no matches and was still able |
- // to suppress verbatim, the user would have no search/nav matches and may be |
- // left unable to search using their default provider from the omnibox. |
- // Check for results on each verbatim calculation, as results from older |
- // queries (on previous input) may be trimmed for failing to inline new input. |
- bool use_server_relevance = |
- (default_results_.verbatim_relevance >= 0) && |
- !input_.prevent_inline_autocomplete() && |
- ((default_results_.verbatim_relevance > 0) || |
- !default_results_.suggest_results.empty() || |
- !default_results_.navigation_results.empty()); |
- if (relevance_from_server) |
- *relevance_from_server = use_server_relevance; |
- return use_server_relevance ? |
- default_results_.verbatim_relevance : CalculateRelevanceForVerbatim(); |
-} |
- |
-int SearchProvider::CalculateRelevanceForVerbatim() const { |
- if (!providers_.keyword_provider().empty()) |
- return 250; |
- return CalculateRelevanceForVerbatimIgnoringKeywordModeState(); |
-} |
- |
-int SearchProvider:: |
- CalculateRelevanceForVerbatimIgnoringKeywordModeState() const { |
- switch (input_.type()) { |
- case metrics::OmniboxInputType::UNKNOWN: |
- case metrics::OmniboxInputType::QUERY: |
- case metrics::OmniboxInputType::FORCED_QUERY: |
- return kNonURLVerbatimRelevance; |
- |
- case metrics::OmniboxInputType::URL: |
- return 850; |
- |
- default: |
- NOTREACHED(); |
- return 0; |
- } |
-} |
- |
-int SearchProvider::GetKeywordVerbatimRelevance( |
- bool* relevance_from_server) const { |
- // Use the suggested verbatim relevance score if it is non-negative (valid), |
- // if inline autocomplete isn't prevented (always show verbatim on backspace), |
- // and if it won't suppress verbatim, leaving no keyword provider matches. |
- // Otherwise, if the keyword provider returned no matches and was still able |
- // to suppress verbatim, the user would have no search/nav matches and may be |
- // left unable to search using their keyword provider from the omnibox. |
- // Check for results on each verbatim calculation, as results from older |
- // queries (on previous input) may be trimmed for failing to inline new input. |
- bool use_server_relevance = |
- (keyword_results_.verbatim_relevance >= 0) && |
- !input_.prevent_inline_autocomplete() && |
- ((keyword_results_.verbatim_relevance > 0) || |
- !keyword_results_.suggest_results.empty() || |
- !keyword_results_.navigation_results.empty()); |
- if (relevance_from_server) |
- *relevance_from_server = use_server_relevance; |
- return use_server_relevance ? |
- keyword_results_.verbatim_relevance : |
- CalculateRelevanceForKeywordVerbatim(keyword_input_.type(), |
- keyword_input_.prefer_keyword()); |
-} |
- |
-int SearchProvider::CalculateRelevanceForHistory( |
- const base::Time& time, |
- bool is_keyword, |
- bool use_aggressive_method, |
- bool prevent_search_history_inlining) const { |
- // The relevance of past searches falls off over time. There are two distinct |
- // equations used. If the first equation is used (searches to the primary |
- // provider that we want to score aggressively), the score is in the range |
- // 1300-1599 (unless |prevent_search_history_inlining|, in which case |
- // it's in the range 1200-1299). If the second equation is used the |
- // relevance of a search 15 minutes ago is discounted 50 points, while the |
- // relevance of a search two weeks ago is discounted 450 points. |
- double elapsed_time = std::max((base::Time::Now() - time).InSecondsF(), 0.0); |
- bool is_primary_provider = is_keyword || !providers_.has_keyword_provider(); |
- if (is_primary_provider && use_aggressive_method) { |
- // Searches with the past two days get a different curve. |
- const double autocomplete_time = 2 * 24 * 60 * 60; |
- if (elapsed_time < autocomplete_time) { |
- int max_score = is_keyword ? 1599 : 1399; |
- if (prevent_search_history_inlining) |
- max_score = 1299; |
- return max_score - static_cast<int>(99 * |
- std::pow(elapsed_time / autocomplete_time, 2.5)); |
- } |
- elapsed_time -= autocomplete_time; |
- } |
- |
- const int score_discount = |
- static_cast<int>(6.5 * std::pow(elapsed_time, 0.3)); |
- |
- // Don't let scores go below 0. Negative relevance scores are meaningful in |
- // a different way. |
- int base_score; |
- if (is_primary_provider) |
- base_score = (input_.type() == metrics::OmniboxInputType::URL) ? 750 : 1050; |
- else |
- base_score = 200; |
- return std::max(0, base_score - score_discount); |
-} |
- |
-AutocompleteMatch SearchProvider::NavigationToMatch( |
- const SearchSuggestionParser::NavigationResult& navigation) { |
- base::string16 input; |
- const bool trimmed_whitespace = base::TrimWhitespace( |
- navigation.from_keyword_provider() ? |
- keyword_input_.text() : input_.text(), |
- base::TRIM_TRAILING, &input) != base::TRIM_NONE; |
- AutocompleteMatch match(this, navigation.relevance(), false, |
- navigation.type()); |
- match.destination_url = navigation.url(); |
- BaseSearchProvider::SetDeletionURL(navigation.deletion_url(), &match); |
- // 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(navigation.formatted_url(), input); |
- size_t match_start = (prefix == NULL) ? |
- navigation.formatted_url().find(input) : prefix->prefix.length(); |
- bool trim_http = !AutocompleteInput::HasHTTPScheme(input) && |
- (!prefix || (match_start != 0)); |
- const net::FormatUrlTypes format_types = |
- net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP); |
- |
- const std::string languages(delegate_->AcceptLanguages()); |
- size_t inline_autocomplete_offset = (prefix == NULL) ? |
- base::string16::npos : (match_start + input.length()); |
- match.fill_into_edit += |
- AutocompleteInput::FormattedStringWithEquivalentMeaning( |
- navigation.url(), |
- net::FormatUrl(navigation.url(), languages, format_types, |
- net::UnescapeRule::SPACES, NULL, NULL, |
- &inline_autocomplete_offset), |
- delegate_->SchemeClassifier()); |
- // Preserve the forced query '?' prefix in |match.fill_into_edit|. |
- // Otherwise, user edits to a suggestion would show non-Search results. |
- if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) { |
- match.fill_into_edit.insert(0, base::ASCIIToUTF16("?")); |
- if (inline_autocomplete_offset != base::string16::npos) |
- ++inline_autocomplete_offset; |
- } |
- if (inline_autocomplete_offset != base::string16::npos) { |
- DCHECK(inline_autocomplete_offset <= match.fill_into_edit.length()); |
- match.inline_autocompletion = |
- match.fill_into_edit.substr(inline_autocomplete_offset); |
- } |
- // An inlineable navsuggestion can only be the default match when there |
- // is no keyword provider active, lest it appear first and break the user |
- // out of keyword mode. It can also only be default if either the inline |
- // autocompletion is empty or we're not preventing inline autocompletion. |
- // Finally, if we have an inlineable navsuggestion with an inline completion |
- // that we're not preventing, make sure we didn't trim any whitespace. |
- // We don't want to claim http://foo.com/bar is inlineable against the |
- // input "foo.com/b ". |
- match.allowed_to_be_default_match = (prefix != NULL) && |
- (providers_.GetKeywordProviderURL() == NULL) && |
- (match.inline_autocompletion.empty() || |
- (!input_.prevent_inline_autocomplete() && !trimmed_whitespace)); |
- match.EnsureUWYTIsAllowedToBeDefault( |
- input_.canonicalized_url(), providers_.template_url_service()); |
- |
- match.contents = navigation.match_contents(); |
- match.contents_class = navigation.match_contents_class(); |
- match.description = navigation.description(); |
- AutocompleteMatch::ClassifyMatchInString(input, match.description, |
- ACMatchClassification::NONE, &match.description_class); |
- |
- match.RecordAdditionalInfo( |
- kRelevanceFromServerKey, |
- navigation.relevance_from_server() ? kTrue : kFalse); |
- match.RecordAdditionalInfo(kShouldPrefetchKey, kFalse); |
- |
- return match; |
-} |
- |
-void SearchProvider::UpdateDone() { |
- // We're done when the timer isn't running, there are no suggest queries |
- // pending, and we're not waiting on Instant. |
- done_ = !timer_.IsRunning() && (suggest_results_pending_ == 0); |
-} |
- |
-std::string SearchProvider::GetSessionToken() { |
- base::TimeTicks current_time(base::TimeTicks::Now()); |
- // Renew token if it expired. |
- if (current_time > token_expiration_time_) { |
- const size_t kTokenBytes = 12; |
- std::string raw_data; |
- base::RandBytes(WriteInto(&raw_data, kTokenBytes + 1), kTokenBytes); |
- base::Base64Encode(raw_data, ¤t_token_); |
- |
- // Make the base64 encoded value URL and filename safe(see RFC 3548). |
- std::replace(current_token_.begin(), current_token_.end(), '+', '-'); |
- std::replace(current_token_.begin(), current_token_.end(), '/', '_'); |
- } |
- |
- // Extend expiration time another 60 seconds. |
- token_expiration_time_ = current_time + base::TimeDelta::FromSeconds(60); |
- |
- return current_token_; |
-} |
- |
-void SearchProvider::RegisterDisplayedAnswers( |
- const AutocompleteResult& result) { |
- if (result.empty()) |
- return; |
- |
- // The answer must be in the first or second slot to be considered. It should |
- // only be in the second slot if AutocompleteController ranked a local search |
- // history or a verbatim item higher than the answer. |
- AutocompleteResult::const_iterator match = result.begin(); |
- if (match->answer_contents.empty() && result.size() > 1) |
- ++match; |
- if (match->answer_contents.empty() || match->answer_type.empty() || |
- match->fill_into_edit.empty()) |
- return; |
- |
- // Valid answer encountered, cache it for further queries. |
- answers_cache_.UpdateRecentAnswers(match->fill_into_edit, match->answer_type); |
-} |
- |
-void SearchProvider::DoAnswersQuery(const AutocompleteInput& input) { |
- prefetch_data_ = answers_cache_.GetTopAnswerEntry(input.text()); |
-} |