Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / components / omnibox / search_provider.cc
blob81dc21aeecab46e0c1ec5e2f850ee1eb98062006
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/omnibox/search_provider.h"
7 #include <algorithm>
8 #include <cmath>
10 #include "base/base64.h"
11 #include "base/callback.h"
12 #include "base/i18n/break_iterator.h"
13 #include "base/i18n/case_conversion.h"
14 #include "base/json/json_string_value_serializer.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/user_metrics.h"
17 #include "base/rand_util.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "components/history/core/browser/in_memory_database.h"
21 #include "components/history/core/browser/keyword_search_term.h"
22 #include "components/metrics/proto/omnibox_input_type.pb.h"
23 #include "components/omnibox/autocomplete_provider_client.h"
24 #include "components/omnibox/autocomplete_provider_listener.h"
25 #include "components/omnibox/autocomplete_result.h"
26 #include "components/omnibox/keyword_provider.h"
27 #include "components/omnibox/omnibox_field_trial.h"
28 #include "components/omnibox/url_prefix.h"
29 #include "components/search/search.h"
30 #include "components/search_engines/template_url_prepopulate_data.h"
31 #include "components/search_engines/template_url_service.h"
32 #include "components/variations/variations_http_header_provider.h"
33 #include "grit/components_strings.h"
34 #include "net/base/escape.h"
35 #include "net/base/load_flags.h"
36 #include "net/base/net_util.h"
37 #include "net/http/http_request_headers.h"
38 #include "net/url_request/url_fetcher.h"
39 #include "net/url_request/url_request_status.h"
40 #include "ui/base/l10n/l10n_util.h"
41 #include "url/url_constants.h"
42 #include "url/url_util.h"
44 // Helpers --------------------------------------------------------------------
46 namespace {
48 // We keep track in a histogram how many suggest requests we send, how
49 // many suggest requests we invalidate (e.g., due to a user typing
50 // another character), and how many replies we receive.
51 // *** ADD NEW ENUMS AFTER ALL PREVIOUSLY DEFINED ONES! ***
52 // (excluding the end-of-list enum value)
53 // We do not want values of existing enums to change or else it screws
54 // up the statistics.
55 enum SuggestRequestsHistogramValue {
56 REQUEST_SENT = 1,
57 REQUEST_INVALIDATED,
58 REPLY_RECEIVED,
59 MAX_SUGGEST_REQUEST_HISTOGRAM_VALUE
62 // The verbatim score for an input which is not an URL.
63 const int kNonURLVerbatimRelevance = 1300;
65 // Increments the appropriate value in the histogram by one.
66 void LogOmniboxSuggestRequest(
67 SuggestRequestsHistogramValue request_value) {
68 UMA_HISTOGRAM_ENUMERATION("Omnibox.SuggestRequests", request_value,
69 MAX_SUGGEST_REQUEST_HISTOGRAM_VALUE);
72 bool HasMultipleWords(const base::string16& text) {
73 base::i18n::BreakIterator i(text, base::i18n::BreakIterator::BREAK_WORD);
74 bool found_word = false;
75 if (i.Init()) {
76 while (i.Advance()) {
77 if (i.IsWord()) {
78 if (found_word)
79 return true;
80 found_word = true;
84 return false;
87 } // namespace
89 // SearchProvider::Providers --------------------------------------------------
91 SearchProvider::Providers::Providers(TemplateURLService* template_url_service)
92 : template_url_service_(template_url_service) {}
94 const TemplateURL* SearchProvider::Providers::GetDefaultProviderURL() const {
95 return default_provider_.empty() ? NULL :
96 template_url_service_->GetTemplateURLForKeyword(default_provider_);
99 const TemplateURL* SearchProvider::Providers::GetKeywordProviderURL() const {
100 return keyword_provider_.empty() ? NULL :
101 template_url_service_->GetTemplateURLForKeyword(keyword_provider_);
105 // SearchProvider::CompareScoredResults ---------------------------------------
107 class SearchProvider::CompareScoredResults {
108 public:
109 bool operator()(const SearchSuggestionParser::Result& a,
110 const SearchSuggestionParser::Result& b) {
111 // Sort in descending relevance order.
112 return a.relevance() > b.relevance();
117 // SearchProvider -------------------------------------------------------------
119 // static
120 int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100;
122 SearchProvider::SearchProvider(
123 AutocompleteProviderListener* listener,
124 TemplateURLService* template_url_service,
125 scoped_ptr<AutocompleteProviderClient> client)
126 : BaseSearchProvider(template_url_service, client.Pass(),
127 AutocompleteProvider::TYPE_SEARCH),
128 listener_(listener),
129 suggest_results_pending_(0),
130 providers_(template_url_service),
131 answers_cache_(1) {
134 // static
135 std::string SearchProvider::GetSuggestMetadata(const AutocompleteMatch& match) {
136 return match.GetAdditionalInfo(kSuggestMetadataKey);
139 void SearchProvider::ResetSession() {
140 field_trial_triggered_in_session_ = false;
143 SearchProvider::~SearchProvider() {
146 // static
147 int SearchProvider::CalculateRelevanceForKeywordVerbatim(
148 metrics::OmniboxInputType::Type type,
149 bool prefer_keyword) {
150 // This function is responsible for scoring verbatim query matches
151 // for non-extension keywords. KeywordProvider::CalculateRelevance()
152 // scores verbatim query matches for extension keywords, as well as
153 // for keyword matches (i.e., suggestions of a keyword itself, not a
154 // suggestion of a query on a keyword search engine). These two
155 // functions are currently in sync, but there's no reason we
156 // couldn't decide in the future to score verbatim matches
157 // differently for extension and non-extension keywords. If you
158 // make such a change, however, you should update this comment to
159 // describe it, so it's clear why the functions diverge.
160 if (prefer_keyword)
161 return 1500;
162 return (type == metrics::OmniboxInputType::QUERY) ? 1450 : 1100;
165 void SearchProvider::Start(const AutocompleteInput& input,
166 bool minimal_changes) {
167 // Do our best to load the model as early as possible. This will reduce
168 // odds of having the model not ready when really needed (a non-empty input).
169 TemplateURLService* model = providers_.template_url_service();
170 DCHECK(model);
171 model->Load();
173 matches_.clear();
174 field_trial_triggered_ = false;
176 // Can't return search/suggest results for bogus input.
177 if (input.type() == metrics::OmniboxInputType::INVALID) {
178 Stop(true);
179 return;
182 keyword_input_ = input;
183 const TemplateURL* keyword_provider =
184 KeywordProvider::GetSubstitutingTemplateURLForInput(model,
185 &keyword_input_);
186 if (keyword_provider == NULL)
187 keyword_input_.Clear();
188 else if (keyword_input_.text().empty())
189 keyword_provider = NULL;
191 const TemplateURL* default_provider = model->GetDefaultSearchProvider();
192 if (default_provider &&
193 !default_provider->SupportsReplacement(model->search_terms_data()))
194 default_provider = NULL;
196 if (keyword_provider == default_provider)
197 default_provider = NULL; // No use in querying the same provider twice.
199 if (!default_provider && !keyword_provider) {
200 // No valid providers.
201 Stop(true);
202 return;
205 // If we're still running an old query but have since changed the query text
206 // or the providers, abort the query.
207 base::string16 default_provider_keyword(default_provider ?
208 default_provider->keyword() : base::string16());
209 base::string16 keyword_provider_keyword(keyword_provider ?
210 keyword_provider->keyword() : base::string16());
211 if (!minimal_changes ||
212 !providers_.equal(default_provider_keyword, keyword_provider_keyword)) {
213 // Cancel any in-flight suggest requests.
214 if (!done_)
215 Stop(false);
218 providers_.set(default_provider_keyword, keyword_provider_keyword);
220 if (input.text().empty()) {
221 // User typed "?" alone. Give them a placeholder result indicating what
222 // this syntax does.
223 if (default_provider) {
224 AutocompleteMatch match;
225 match.provider = this;
226 match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE));
227 match.contents_class.push_back(
228 ACMatchClassification(0, ACMatchClassification::NONE));
229 match.keyword = providers_.default_provider();
230 match.allowed_to_be_default_match = true;
231 matches_.push_back(match);
233 Stop(true);
234 return;
237 input_ = input;
239 DoHistoryQuery(minimal_changes);
240 DoAnswersQuery(input);
241 StartOrStopSuggestQuery(minimal_changes);
242 UpdateMatches();
245 void SearchProvider::Stop(bool clear_cached_results) {
246 StopSuggest();
247 done_ = true;
249 if (clear_cached_results)
250 ClearAllResults();
253 const TemplateURL* SearchProvider::GetTemplateURL(bool is_keyword) const {
254 return is_keyword ? providers_.GetKeywordProviderURL()
255 : providers_.GetDefaultProviderURL();
258 const AutocompleteInput SearchProvider::GetInput(bool is_keyword) const {
259 return is_keyword ? keyword_input_ : input_;
262 bool SearchProvider::ShouldAppendExtraParams(
263 const SearchSuggestionParser::SuggestResult& result) const {
264 return !result.from_keyword_provider() ||
265 providers_.default_provider().empty();
268 void SearchProvider::RecordDeletionResult(bool success) {
269 if (success) {
270 base::RecordAction(
271 base::UserMetricsAction("Omnibox.ServerSuggestDelete.Success"));
272 } else {
273 base::RecordAction(
274 base::UserMetricsAction("Omnibox.ServerSuggestDelete.Failure"));
278 void SearchProvider::OnURLFetchComplete(const net::URLFetcher* source) {
279 DCHECK(!done_);
280 --suggest_results_pending_;
281 DCHECK_GE(suggest_results_pending_, 0); // Should never go negative.
283 const bool is_keyword = source == keyword_fetcher_.get();
285 // Ensure the request succeeded and that the provider used is still available.
286 // A verbatim match cannot be generated without this provider, causing errors.
287 const bool request_succeeded =
288 source->GetStatus().is_success() && (source->GetResponseCode() == 200) &&
289 GetTemplateURL(is_keyword);
291 LogFetchComplete(request_succeeded, is_keyword);
293 bool results_updated = false;
294 if (request_succeeded) {
295 scoped_ptr<base::Value> data(SearchSuggestionParser::DeserializeJsonData(
296 SearchSuggestionParser::ExtractJsonData(source)));
297 if (data) {
298 SearchSuggestionParser::Results* results =
299 is_keyword ? &keyword_results_ : &default_results_;
300 results_updated = ParseSuggestResults(*data, -1, is_keyword, results);
301 if (results_updated)
302 SortResults(is_keyword, results);
305 UpdateMatches();
306 if (done_ || results_updated)
307 listener_->OnProviderUpdate(results_updated);
310 void SearchProvider::StopSuggest() {
311 // Increment the appropriate field in the histogram by the number of
312 // pending requests that were invalidated.
313 for (int i = 0; i < suggest_results_pending_; ++i)
314 LogOmniboxSuggestRequest(REQUEST_INVALIDATED);
315 suggest_results_pending_ = 0;
316 timer_.Stop();
317 // Stop any in-progress URL fetches.
318 keyword_fetcher_.reset();
319 default_fetcher_.reset();
322 void SearchProvider::ClearAllResults() {
323 keyword_results_.Clear();
324 default_results_.Clear();
327 void SearchProvider::UpdateMatchContentsClass(
328 const base::string16& input_text,
329 SearchSuggestionParser::Results* results) {
330 for (SearchSuggestionParser::SuggestResults::iterator sug_it =
331 results->suggest_results.begin();
332 sug_it != results->suggest_results.end(); ++sug_it) {
333 sug_it->ClassifyMatchContents(false, input_text);
335 const std::string languages(client_->AcceptLanguages());
336 for (SearchSuggestionParser::NavigationResults::iterator nav_it =
337 results->navigation_results.begin();
338 nav_it != results->navigation_results.end(); ++nav_it) {
339 nav_it->CalculateAndClassifyMatchContents(false, input_text, languages);
343 void SearchProvider::SortResults(bool is_keyword,
344 SearchSuggestionParser::Results* results) {
345 // Ignore suggested scores for non-keyword matches in keyword mode; if the
346 // server is allowed to score these, it could interfere with the user's
347 // ability to get good keyword results.
348 const bool abandon_suggested_scores =
349 !is_keyword && !providers_.keyword_provider().empty();
350 // Apply calculated relevance scores to suggestions if valid relevances were
351 // not provided or we're abandoning suggested scores entirely.
352 if (!results->relevances_from_server || abandon_suggested_scores) {
353 ApplyCalculatedSuggestRelevance(&results->suggest_results);
354 ApplyCalculatedNavigationRelevance(&results->navigation_results);
355 // If abandoning scores entirely, also abandon the verbatim score.
356 if (abandon_suggested_scores)
357 results->verbatim_relevance = -1;
360 // Keep the result lists sorted.
361 const CompareScoredResults comparator = CompareScoredResults();
362 std::stable_sort(results->suggest_results.begin(),
363 results->suggest_results.end(),
364 comparator);
365 std::stable_sort(results->navigation_results.begin(),
366 results->navigation_results.end(),
367 comparator);
370 void SearchProvider::LogFetchComplete(bool success, bool is_keyword) {
371 LogOmniboxSuggestRequest(REPLY_RECEIVED);
372 // Record response time for suggest requests sent to Google. We care
373 // only about the common case: the Google default provider used in
374 // non-keyword mode.
375 const TemplateURL* default_url = providers_.GetDefaultProviderURL();
376 if (!is_keyword && default_url &&
377 (TemplateURLPrepopulateData::GetEngineType(
378 *default_url,
379 providers_.template_url_service()->search_terms_data()) ==
380 SEARCH_ENGINE_GOOGLE)) {
381 const base::TimeDelta elapsed_time =
382 base::TimeTicks::Now() - time_suggest_request_sent_;
383 if (success) {
384 UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Success.GoogleResponseTime",
385 elapsed_time);
386 } else {
387 UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Failure.GoogleResponseTime",
388 elapsed_time);
393 void SearchProvider::UpdateMatches() {
394 ConvertResultsToAutocompleteMatches();
396 // Check constraints that may be violated by suggested relevances.
397 if (!matches_.empty() &&
398 (default_results_.HasServerProvidedScores() ||
399 keyword_results_.HasServerProvidedScores())) {
400 // These blocks attempt to repair undesirable behavior by suggested
401 // relevances with minimal impact, preserving other suggested relevances.
403 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
404 const bool is_extension_keyword = (keyword_url != NULL) &&
405 (keyword_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION);
406 if ((keyword_url != NULL) && !is_extension_keyword &&
407 (FindTopMatch() == matches_.end())) {
408 // In non-extension keyword mode, disregard the keyword verbatim suggested
409 // relevance if necessary, so at least one match is allowed to be default.
410 // (In extension keyword mode this is not necessary because the extension
411 // will return a default match.)
412 keyword_results_.verbatim_relevance = -1;
413 ConvertResultsToAutocompleteMatches();
415 if (IsTopMatchSearchWithURLInput()) {
416 // Disregard the suggested search and verbatim relevances if the input
417 // type is URL and the top match is a highly-ranked search suggestion.
418 // For example, prevent a search for "foo.com" from outranking another
419 // provider's navigation for "foo.com" or "foo.com/url_from_history".
420 ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results);
421 ApplyCalculatedSuggestRelevance(&default_results_.suggest_results);
422 default_results_.verbatim_relevance = -1;
423 keyword_results_.verbatim_relevance = -1;
424 ConvertResultsToAutocompleteMatches();
426 if (!is_extension_keyword && (FindTopMatch() == matches_.end())) {
427 // Guarantee that SearchProvider returns a legal default match (except
428 // when in extension-based keyword mode). The omnibox always needs at
429 // least one legal default match, and it relies on SearchProvider in
430 // combination with KeywordProvider (for extension-based keywords) to
431 // always return one.
432 ApplyCalculatedRelevance();
433 ConvertResultsToAutocompleteMatches();
435 DCHECK(!IsTopMatchSearchWithURLInput());
436 DCHECK(is_extension_keyword || (FindTopMatch() != matches_.end()));
438 UMA_HISTOGRAM_CUSTOM_COUNTS(
439 "Omnibox.SearchProviderMatches", matches_.size(), 1, 6, 7);
440 UpdateDone();
443 void SearchProvider::Run() {
444 // Start a new request with the current input.
445 suggest_results_pending_ = 0;
446 time_suggest_request_sent_ = base::TimeTicks::Now();
448 default_fetcher_.reset(CreateSuggestFetcher(kDefaultProviderURLFetcherID,
449 providers_.GetDefaultProviderURL(), input_));
450 keyword_fetcher_.reset(CreateSuggestFetcher(kKeywordProviderURLFetcherID,
451 providers_.GetKeywordProviderURL(), keyword_input_));
453 // Both the above can fail if the providers have been modified or deleted
454 // since the query began.
455 if (suggest_results_pending_ == 0) {
456 UpdateDone();
457 // We only need to update the listener if we're actually done.
458 if (done_)
459 listener_->OnProviderUpdate(false);
463 void SearchProvider::DoHistoryQuery(bool minimal_changes) {
464 // The history query results are synchronous, so if minimal_changes is true,
465 // we still have the last results and don't need to do anything.
466 if (minimal_changes)
467 return;
469 keyword_history_results_.clear();
470 default_history_results_.clear();
472 if (OmniboxFieldTrial::SearchHistoryDisable(
473 input_.current_page_classification()))
474 return;
476 history::URLDatabase* url_db = client_->InMemoryDatabase();
477 if (!url_db)
478 return;
480 // Request history for both the keyword and default provider. We grab many
481 // more matches than we'll ultimately clamp to so that if there are several
482 // recent multi-word matches who scores are lowered (see
483 // AddHistoryResultsToMap()), they won't crowd out older, higher-scoring
484 // matches. Note that this doesn't fix the problem entirely, but merely
485 // limits it to cases with a very large number of such multi-word matches; for
486 // now, this seems OK compared with the complexity of a real fix, which would
487 // require multiple searches and tracking of "single- vs. multi-word" in the
488 // database.
489 int num_matches = kMaxMatches * 5;
490 const TemplateURL* default_url = providers_.GetDefaultProviderURL();
491 if (default_url) {
492 const base::TimeTicks start_time = base::TimeTicks::Now();
493 url_db->GetMostRecentKeywordSearchTerms(default_url->id(), input_.text(),
494 num_matches, &default_history_results_);
495 UMA_HISTOGRAM_TIMES(
496 "Omnibox.SearchProvider.GetMostRecentKeywordTermsDefaultProviderTime",
497 base::TimeTicks::Now() - start_time);
499 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
500 if (keyword_url) {
501 url_db->GetMostRecentKeywordSearchTerms(keyword_url->id(),
502 keyword_input_.text(), num_matches, &keyword_history_results_);
506 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) {
507 if (!IsQuerySuitableForSuggest()) {
508 StopSuggest();
509 ClearAllResults();
510 return;
513 // For the minimal_changes case, if we finished the previous query and still
514 // have its results, or are allowed to keep running it, just do that, rather
515 // than starting a new query.
516 if (minimal_changes &&
517 (!default_results_.suggest_results.empty() ||
518 !default_results_.navigation_results.empty() ||
519 !keyword_results_.suggest_results.empty() ||
520 !keyword_results_.navigation_results.empty() ||
521 (!done_ && input_.want_asynchronous_matches())))
522 return;
524 // We can't keep running any previous query, so halt it.
525 StopSuggest();
527 // Remove existing results that cannot inline autocomplete the new input.
528 RemoveAllStaleResults();
530 // Update the content classifications of remaining results so they look good
531 // against the current input.
532 UpdateMatchContentsClass(input_.text(), &default_results_);
533 if (!keyword_input_.text().empty())
534 UpdateMatchContentsClass(keyword_input_.text(), &keyword_results_);
536 // We can't start a new query if we're only allowed synchronous results.
537 if (!input_.want_asynchronous_matches())
538 return;
540 // To avoid flooding the suggest server, don't send a query until at
541 // least 100 ms since the last query.
542 base::TimeTicks next_suggest_time(time_suggest_request_sent_ +
543 base::TimeDelta::FromMilliseconds(kMinimumTimeBetweenSuggestQueriesMs));
544 base::TimeTicks now(base::TimeTicks::Now());
545 if (now >= next_suggest_time) {
546 Run();
547 return;
549 timer_.Start(FROM_HERE, next_suggest_time - now, this, &SearchProvider::Run);
552 bool SearchProvider::IsQuerySuitableForSuggest() const {
553 // Don't run Suggest in incognito mode, if the engine doesn't support it, or
554 // if the user has disabled it.
555 const TemplateURL* default_url = providers_.GetDefaultProviderURL();
556 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
557 if (client_->IsOffTheRecord() ||
558 ((!default_url || default_url->suggestions_url().empty()) &&
559 (!keyword_url || keyword_url->suggestions_url().empty())) ||
560 !client_->SearchSuggestEnabled())
561 return false;
563 // If the input type might be a URL, we take extra care so that private data
564 // isn't sent to the server.
566 // FORCED_QUERY means the user is explicitly asking us to search for this, so
567 // we assume it isn't a URL and/or there isn't private data.
568 if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY)
569 return true;
571 // Next we check the scheme. If this is UNKNOWN/URL with a scheme that isn't
572 // http/https/ftp, we shouldn't send it. Sending things like file: and data:
573 // is both a waste of time and a disclosure of potentially private, local
574 // data. Other "schemes" may actually be usernames, and we don't want to send
575 // passwords. If the scheme is OK, we still need to check other cases below.
576 // If this is QUERY, then the presence of these schemes means the user
577 // explicitly typed one, and thus this is probably a URL that's being entered
578 // and happens to currently be invalid -- in which case we again want to run
579 // our checks below. Other QUERY cases are less likely to be URLs and thus we
580 // assume we're OK.
581 if (!LowerCaseEqualsASCII(input_.scheme(), url::kHttpScheme) &&
582 !LowerCaseEqualsASCII(input_.scheme(), url::kHttpsScheme) &&
583 !LowerCaseEqualsASCII(input_.scheme(), url::kFtpScheme))
584 return (input_.type() == metrics::OmniboxInputType::QUERY);
586 // Don't send URLs with usernames, queries or refs. Some of these are
587 // private, and the Suggest server is unlikely to have any useful results
588 // for any of them. Also don't send URLs with ports, as we may initially
589 // think that a username + password is a host + port (and we don't want to
590 // send usernames/passwords), and even if the port really is a port, the
591 // server is once again unlikely to have and useful results.
592 // Note that we only block based on refs if the input is URL-typed, as search
593 // queries can legitimately have #s in them which the URL parser
594 // overaggressively categorizes as a url with a ref.
595 const url::Parsed& parts = input_.parts();
596 if (parts.username.is_nonempty() || parts.port.is_nonempty() ||
597 parts.query.is_nonempty() ||
598 (parts.ref.is_nonempty() &&
599 (input_.type() == metrics::OmniboxInputType::URL)))
600 return false;
602 // Don't send anything for https except the hostname. Hostnames are OK
603 // because they are visible when the TCP connection is established, but the
604 // specific path may reveal private information.
605 if (LowerCaseEqualsASCII(input_.scheme(), url::kHttpsScheme) &&
606 parts.path.is_nonempty())
607 return false;
609 return true;
612 void SearchProvider::RemoveAllStaleResults() {
613 if (keyword_input_.text().empty()) {
614 // User is either in keyword mode with a blank input or out of
615 // keyword mode entirely.
616 keyword_results_.Clear();
620 void SearchProvider::ApplyCalculatedRelevance() {
621 ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results);
622 ApplyCalculatedSuggestRelevance(&default_results_.suggest_results);
623 ApplyCalculatedNavigationRelevance(&keyword_results_.navigation_results);
624 ApplyCalculatedNavigationRelevance(&default_results_.navigation_results);
625 default_results_.verbatim_relevance = -1;
626 keyword_results_.verbatim_relevance = -1;
629 void SearchProvider::ApplyCalculatedSuggestRelevance(
630 SearchSuggestionParser::SuggestResults* list) {
631 for (size_t i = 0; i < list->size(); ++i) {
632 SearchSuggestionParser::SuggestResult& result = (*list)[i];
633 result.set_relevance(
634 result.CalculateRelevance(input_, providers_.has_keyword_provider()) +
635 (list->size() - i - 1));
636 result.set_relevance_from_server(false);
640 void SearchProvider::ApplyCalculatedNavigationRelevance(
641 SearchSuggestionParser::NavigationResults* list) {
642 for (size_t i = 0; i < list->size(); ++i) {
643 SearchSuggestionParser::NavigationResult& result = (*list)[i];
644 result.set_relevance(
645 result.CalculateRelevance(input_, providers_.has_keyword_provider()) +
646 (list->size() - i - 1));
647 result.set_relevance_from_server(false);
651 net::URLFetcher* SearchProvider::CreateSuggestFetcher(
652 int id,
653 const TemplateURL* template_url,
654 const AutocompleteInput& input) {
655 if (!template_url || template_url->suggestions_url().empty())
656 return NULL;
658 // Bail if the suggestion URL is invalid with the given replacements.
659 TemplateURLRef::SearchTermsArgs search_term_args(input.text());
660 search_term_args.input_type = input.type();
661 search_term_args.cursor_position = input.cursor_position();
662 search_term_args.page_classification = input.current_page_classification();
663 if (OmniboxFieldTrial::EnableAnswersInSuggest()) {
664 search_term_args.session_token = GetSessionToken();
665 if (!prefetch_data_.full_query_text.empty()) {
666 search_term_args.prefetch_query =
667 base::UTF16ToUTF8(prefetch_data_.full_query_text);
668 search_term_args.prefetch_query_type =
669 base::UTF16ToUTF8(prefetch_data_.query_type);
672 GURL suggest_url(template_url->suggestions_url_ref().ReplaceSearchTerms(
673 search_term_args,
674 providers_.template_url_service()->search_terms_data()));
675 if (!suggest_url.is_valid())
676 return NULL;
677 // Send the current page URL if user setting and URL requirements are met and
678 // the user is in the field trial.
679 if (CanSendURL(current_page_url_, suggest_url, template_url,
680 input.current_page_classification(),
681 template_url_service_->search_terms_data(), client_.get()) &&
682 OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()) {
683 search_term_args.current_page_url = current_page_url_.spec();
684 // Create the suggest URL again with the current page URL.
685 suggest_url = GURL(template_url->suggestions_url_ref().ReplaceSearchTerms(
686 search_term_args,
687 providers_.template_url_service()->search_terms_data()));
690 suggest_results_pending_++;
691 LogOmniboxSuggestRequest(REQUEST_SENT);
693 net::URLFetcher* fetcher =
694 net::URLFetcher::Create(id, suggest_url, net::URLFetcher::GET, this);
695 fetcher->SetRequestContext(client_->RequestContext());
696 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
697 // Add Chrome experiment state to the request headers.
698 net::HttpRequestHeaders headers;
699 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders(
700 fetcher->GetOriginalURL(), client_->IsOffTheRecord(), false, &headers);
701 fetcher->SetExtraRequestHeaders(headers.ToString());
702 fetcher->Start();
703 return fetcher;
706 void SearchProvider::ConvertResultsToAutocompleteMatches() {
707 // Convert all the results to matches and add them to a map, so we can keep
708 // the most relevant match for each result.
709 base::TimeTicks start_time(base::TimeTicks::Now());
710 MatchMap map;
711 const base::Time no_time;
712 int did_not_accept_keyword_suggestion =
713 keyword_results_.suggest_results.empty() ?
714 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE :
715 TemplateURLRef::NO_SUGGESTION_CHOSEN;
717 bool relevance_from_server;
718 int verbatim_relevance = GetVerbatimRelevance(&relevance_from_server);
719 int did_not_accept_default_suggestion =
720 default_results_.suggest_results.empty() ?
721 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE :
722 TemplateURLRef::NO_SUGGESTION_CHOSEN;
723 const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
724 if (verbatim_relevance > 0) {
725 const base::string16& trimmed_verbatim =
726 base::CollapseWhitespace(input_.text(), false);
728 // Verbatim results don't get suggestions and hence, answers.
729 // Scan previous matches if the last answer-bearing suggestion matches
730 // verbatim, and if so, copy over answer contents.
731 base::string16 answer_contents;
732 base::string16 answer_type;
733 for (ACMatches::iterator it = matches_.begin(); it != matches_.end();
734 ++it) {
735 if (!it->answer_contents.empty() &&
736 it->fill_into_edit == trimmed_verbatim) {
737 answer_contents = it->answer_contents;
738 answer_type = it->answer_type;
739 break;
743 SearchSuggestionParser::SuggestResult verbatim(
744 trimmed_verbatim, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
745 trimmed_verbatim, base::string16(), base::string16(), answer_contents,
746 answer_type, std::string(), std::string(), false, verbatim_relevance,
747 relevance_from_server, false, trimmed_verbatim);
748 AddMatchToMap(verbatim, std::string(), did_not_accept_default_suggestion,
749 false, keyword_url != NULL, &map);
751 if (!keyword_input_.text().empty()) {
752 // We only create the verbatim search query match for a keyword
753 // if it's not an extension keyword. Extension keywords are handled
754 // in KeywordProvider::Start(). (Extensions are complicated...)
755 // Note: in this provider, SEARCH_OTHER_ENGINE must correspond
756 // to the keyword verbatim search query. Do not create other matches
757 // of type SEARCH_OTHER_ENGINE.
758 if (keyword_url &&
759 (keyword_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION)) {
760 bool keyword_relevance_from_server;
761 const int keyword_verbatim_relevance =
762 GetKeywordVerbatimRelevance(&keyword_relevance_from_server);
763 if (keyword_verbatim_relevance > 0) {
764 const base::string16& trimmed_verbatim =
765 base::CollapseWhitespace(keyword_input_.text(), false);
766 SearchSuggestionParser::SuggestResult verbatim(
767 trimmed_verbatim, AutocompleteMatchType::SEARCH_OTHER_ENGINE,
768 trimmed_verbatim, base::string16(), base::string16(),
769 base::string16(), base::string16(), std::string(), std::string(),
770 true, keyword_verbatim_relevance, keyword_relevance_from_server,
771 false, trimmed_verbatim);
772 AddMatchToMap(verbatim, std::string(),
773 did_not_accept_keyword_suggestion, false, true, &map);
777 AddHistoryResultsToMap(keyword_history_results_, true,
778 did_not_accept_keyword_suggestion, &map);
779 AddHistoryResultsToMap(default_history_results_, false,
780 did_not_accept_default_suggestion, &map);
782 AddSuggestResultsToMap(keyword_results_.suggest_results,
783 keyword_results_.metadata, &map);
784 AddSuggestResultsToMap(default_results_.suggest_results,
785 default_results_.metadata, &map);
787 ACMatches matches;
788 for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i)
789 matches.push_back(i->second);
791 AddNavigationResultsToMatches(keyword_results_.navigation_results, &matches);
792 AddNavigationResultsToMatches(default_results_.navigation_results, &matches);
794 // Now add the most relevant matches to |matches_|. We take up to kMaxMatches
795 // suggest/navsuggest matches, regardless of origin. If Instant Extended is
796 // enabled and we have server-provided (and thus hopefully more accurate)
797 // scores for some suggestions, we allow more of those, until we reach
798 // AutocompleteResult::kMaxMatches total matches (that is, enough to fill the
799 // whole popup).
801 // We will always return any verbatim matches, no matter how we obtained their
802 // scores, unless we have already accepted AutocompleteResult::kMaxMatches
803 // higher-scoring matches under the conditions above.
804 std::sort(matches.begin(), matches.end(), &AutocompleteMatch::MoreRelevant);
805 matches_.clear();
807 size_t num_suggestions = 0;
808 for (ACMatches::const_iterator i(matches.begin());
809 (i != matches.end()) &&
810 (matches_.size() < AutocompleteResult::kMaxMatches);
811 ++i) {
812 // SEARCH_OTHER_ENGINE is only used in the SearchProvider for the keyword
813 // verbatim result, so this condition basically means "if this match is a
814 // suggestion of some sort".
815 if ((i->type != AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED) &&
816 (i->type != AutocompleteMatchType::SEARCH_OTHER_ENGINE)) {
817 // If we've already hit the limit on non-server-scored suggestions, and
818 // this isn't a server-scored suggestion we can add, skip it.
819 if ((num_suggestions >= kMaxMatches) &&
820 (!chrome::IsInstantExtendedAPIEnabled() ||
821 (i->GetAdditionalInfo(kRelevanceFromServerKey) != kTrue))) {
822 continue;
825 ++num_suggestions;
828 matches_.push_back(*i);
830 UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.ConvertResultsTime",
831 base::TimeTicks::Now() - start_time);
834 ACMatches::const_iterator SearchProvider::FindTopMatch() const {
835 ACMatches::const_iterator it = matches_.begin();
836 while ((it != matches_.end()) && !it->allowed_to_be_default_match)
837 ++it;
838 return it;
841 bool SearchProvider::IsTopMatchSearchWithURLInput() const {
842 ACMatches::const_iterator first_match = FindTopMatch();
843 return (input_.type() == metrics::OmniboxInputType::URL) &&
844 (first_match != matches_.end()) &&
845 (first_match->relevance > CalculateRelevanceForVerbatim()) &&
846 (first_match->type != AutocompleteMatchType::NAVSUGGEST) &&
847 (first_match->type != AutocompleteMatchType::NAVSUGGEST_PERSONALIZED);
850 void SearchProvider::AddNavigationResultsToMatches(
851 const SearchSuggestionParser::NavigationResults& navigation_results,
852 ACMatches* matches) {
853 for (SearchSuggestionParser::NavigationResults::const_iterator it =
854 navigation_results.begin(); it != navigation_results.end(); ++it) {
855 matches->push_back(NavigationToMatch(*it));
856 // In the absence of suggested relevance scores, use only the single
857 // highest-scoring result. (The results are already sorted by relevance.)
858 if (!it->relevance_from_server())
859 return;
863 void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results,
864 bool is_keyword,
865 int did_not_accept_suggestion,
866 MatchMap* map) {
867 if (results.empty())
868 return;
870 base::TimeTicks start_time(base::TimeTicks::Now());
871 bool prevent_inline_autocomplete = input_.prevent_inline_autocomplete() ||
872 (input_.type() == metrics::OmniboxInputType::URL);
873 const base::string16& input_text =
874 is_keyword ? keyword_input_.text() : input_.text();
875 bool input_multiple_words = HasMultipleWords(input_text);
877 SearchSuggestionParser::SuggestResults scored_results;
878 if (!prevent_inline_autocomplete && input_multiple_words) {
879 // ScoreHistoryResults() allows autocompletion of multi-word, 1-visit
880 // queries if the input also has multiple words. But if we were already
881 // scoring a multi-word, multi-visit query aggressively, and the current
882 // input is still a prefix of it, then changing the suggestion suddenly
883 // feels wrong. To detect this case, first score as if only one word has
884 // been typed, then check if the best result came from aggressive search
885 // history scoring. If it did, then just keep that score set. This
886 // 1200 the lowest possible score in CalculateRelevanceForHistory()'s
887 // aggressive-scoring curve.
888 scored_results = ScoreHistoryResults(results, prevent_inline_autocomplete,
889 false, input_text, is_keyword);
890 if ((scored_results.front().relevance() < 1200) ||
891 !HasMultipleWords(scored_results.front().suggestion()))
892 scored_results.clear(); // Didn't detect the case above, score normally.
894 if (scored_results.empty())
895 scored_results = ScoreHistoryResults(results, prevent_inline_autocomplete,
896 input_multiple_words, input_text,
897 is_keyword);
898 for (SearchSuggestionParser::SuggestResults::const_iterator i(
899 scored_results.begin()); i != scored_results.end(); ++i) {
900 AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true,
901 providers_.GetKeywordProviderURL() != NULL, map);
903 UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime",
904 base::TimeTicks::Now() - start_time);
907 SearchSuggestionParser::SuggestResults SearchProvider::ScoreHistoryResults(
908 const HistoryResults& results,
909 bool base_prevent_inline_autocomplete,
910 bool input_multiple_words,
911 const base::string16& input_text,
912 bool is_keyword) {
913 SearchSuggestionParser::SuggestResults scored_results;
914 // True if the user has asked this exact query previously.
915 bool found_what_you_typed_match = false;
916 const bool prevent_search_history_inlining =
917 OmniboxFieldTrial::SearchHistoryPreventInlining(
918 input_.current_page_classification());
919 const base::string16& trimmed_input =
920 base::CollapseWhitespace(input_text, false);
921 for (HistoryResults::const_iterator i(results.begin()); i != results.end();
922 ++i) {
923 const base::string16& trimmed_suggestion =
924 base::CollapseWhitespace(i->term, false);
926 // Don't autocomplete multi-word queries that have only been seen once
927 // unless the user has typed more than one word.
928 bool prevent_inline_autocomplete = base_prevent_inline_autocomplete ||
929 (!input_multiple_words && (i->visits < 2) &&
930 HasMultipleWords(trimmed_suggestion));
932 int relevance = CalculateRelevanceForHistory(
933 i->time, is_keyword, !prevent_inline_autocomplete,
934 prevent_search_history_inlining);
935 // Add the match to |scored_results| by putting the what-you-typed match
936 // on the front and appending all other matches. We want the what-you-
937 // typed match to always be first.
938 SearchSuggestionParser::SuggestResults::iterator insertion_position =
939 scored_results.end();
940 if (trimmed_suggestion == trimmed_input) {
941 found_what_you_typed_match = true;
942 insertion_position = scored_results.begin();
944 scored_results.insert(
945 insertion_position,
946 SearchSuggestionParser::SuggestResult(
947 trimmed_suggestion, AutocompleteMatchType::SEARCH_HISTORY,
948 trimmed_suggestion, base::string16(), base::string16(),
949 base::string16(), base::string16(), std::string(), std::string(),
950 is_keyword, relevance, false, false, trimmed_input));
953 // History returns results sorted for us. However, we may have docked some
954 // results' scores, so things are no longer in order. While keeping the
955 // what-you-typed match at the front (if it exists), do a stable sort to get
956 // things back in order without otherwise disturbing results with equal
957 // scores, then force the scores to be unique, so that the order in which
958 // they're shown is deterministic.
959 std::stable_sort(scored_results.begin() +
960 (found_what_you_typed_match ? 1 : 0),
961 scored_results.end(),
962 CompareScoredResults());
964 // Don't autocomplete to search terms that would normally be treated as URLs
965 // when typed. For example, if the user searched for "google.com" and types
966 // "goog", don't autocomplete to the search term "google.com". Otherwise,
967 // the input will look like a URL but act like a search, which is confusing.
968 // The 1200 relevance score threshold in the test below is the lowest
969 // possible score in CalculateRelevanceForHistory()'s aggressive-scoring
970 // curve. This is an appropriate threshold to use to decide if we're overly
971 // aggressively inlining because, if we decide the answer is yes, the
972 // way we resolve it it to not use the aggressive-scoring curve.
973 // NOTE: We don't check for autocompleting to URLs in the following cases:
974 // * When inline autocomplete is disabled, we won't be inline autocompleting
975 // this term, so we don't need to worry about confusion as much. This
976 // also prevents calling Classify() again from inside the classifier
977 // (which will corrupt state and likely crash), since the classifier
978 // always disables inline autocomplete.
979 // * When the user has typed the whole string before as a query, then it's
980 // likely the user has no expectation that term should be interpreted as
981 // as a URL, so we need not do anything special to preserve user
982 // expectation.
983 int last_relevance = 0;
984 if (!base_prevent_inline_autocomplete && !found_what_you_typed_match &&
985 scored_results.front().relevance() >= 1200) {
986 AutocompleteMatch match;
987 client_->Classify(scored_results.front().suggestion(), false, false,
988 input_.current_page_classification(), &match, NULL);
989 // Demote this match that would normally be interpreted as a URL to have
990 // the highest score a previously-issued search query could have when
991 // scoring with the non-aggressive method. A consequence of demoting
992 // by revising |last_relevance| is that this match and all following
993 // matches get demoted; the relative order of matches is preserved.
994 // One could imagine demoting only those matches that might cause
995 // confusion (which, by the way, might change the relative order of
996 // matches. We have decided to go with the simple demote-all approach
997 // because selective demotion requires multiple Classify() calls and
998 // such calls can be expensive (as expensive as running the whole
999 // autocomplete system).
1000 if (!AutocompleteMatch::IsSearchType(match.type)) {
1001 last_relevance = CalculateRelevanceForHistory(
1002 base::Time::Now(), is_keyword, false,
1003 prevent_search_history_inlining);
1007 for (SearchSuggestionParser::SuggestResults::iterator i(
1008 scored_results.begin()); i != scored_results.end(); ++i) {
1009 if ((last_relevance != 0) && (i->relevance() >= last_relevance))
1010 i->set_relevance(last_relevance - 1);
1011 last_relevance = i->relevance();
1014 return scored_results;
1017 void SearchProvider::AddSuggestResultsToMap(
1018 const SearchSuggestionParser::SuggestResults& results,
1019 const std::string& metadata,
1020 MatchMap* map) {
1021 for (size_t i = 0; i < results.size(); ++i) {
1022 AddMatchToMap(results[i], metadata, i, false,
1023 providers_.GetKeywordProviderURL() != NULL, map);
1027 int SearchProvider::GetVerbatimRelevance(bool* relevance_from_server) const {
1028 // Use the suggested verbatim relevance score if it is non-negative (valid),
1029 // if inline autocomplete isn't prevented (always show verbatim on backspace),
1030 // and if it won't suppress verbatim, leaving no default provider matches.
1031 // Otherwise, if the default provider returned no matches and was still able
1032 // to suppress verbatim, the user would have no search/nav matches and may be
1033 // left unable to search using their default provider from the omnibox.
1034 // Check for results on each verbatim calculation, as results from older
1035 // queries (on previous input) may be trimmed for failing to inline new input.
1036 bool use_server_relevance =
1037 (default_results_.verbatim_relevance >= 0) &&
1038 !input_.prevent_inline_autocomplete() &&
1039 ((default_results_.verbatim_relevance > 0) ||
1040 !default_results_.suggest_results.empty() ||
1041 !default_results_.navigation_results.empty());
1042 if (relevance_from_server)
1043 *relevance_from_server = use_server_relevance;
1044 return use_server_relevance ?
1045 default_results_.verbatim_relevance : CalculateRelevanceForVerbatim();
1048 int SearchProvider::CalculateRelevanceForVerbatim() const {
1049 if (!providers_.keyword_provider().empty())
1050 return 250;
1051 return CalculateRelevanceForVerbatimIgnoringKeywordModeState();
1054 int SearchProvider::
1055 CalculateRelevanceForVerbatimIgnoringKeywordModeState() const {
1056 switch (input_.type()) {
1057 case metrics::OmniboxInputType::UNKNOWN:
1058 case metrics::OmniboxInputType::QUERY:
1059 case metrics::OmniboxInputType::FORCED_QUERY:
1060 return kNonURLVerbatimRelevance;
1062 case metrics::OmniboxInputType::URL:
1063 return 850;
1065 default:
1066 NOTREACHED();
1067 return 0;
1071 int SearchProvider::GetKeywordVerbatimRelevance(
1072 bool* relevance_from_server) const {
1073 // Use the suggested verbatim relevance score if it is non-negative (valid),
1074 // if inline autocomplete isn't prevented (always show verbatim on backspace),
1075 // and if it won't suppress verbatim, leaving no keyword provider matches.
1076 // Otherwise, if the keyword provider returned no matches and was still able
1077 // to suppress verbatim, the user would have no search/nav matches and may be
1078 // left unable to search using their keyword provider from the omnibox.
1079 // Check for results on each verbatim calculation, as results from older
1080 // queries (on previous input) may be trimmed for failing to inline new input.
1081 bool use_server_relevance =
1082 (keyword_results_.verbatim_relevance >= 0) &&
1083 !input_.prevent_inline_autocomplete() &&
1084 ((keyword_results_.verbatim_relevance > 0) ||
1085 !keyword_results_.suggest_results.empty() ||
1086 !keyword_results_.navigation_results.empty());
1087 if (relevance_from_server)
1088 *relevance_from_server = use_server_relevance;
1089 return use_server_relevance ?
1090 keyword_results_.verbatim_relevance :
1091 CalculateRelevanceForKeywordVerbatim(keyword_input_.type(),
1092 keyword_input_.prefer_keyword());
1095 int SearchProvider::CalculateRelevanceForHistory(
1096 const base::Time& time,
1097 bool is_keyword,
1098 bool use_aggressive_method,
1099 bool prevent_search_history_inlining) const {
1100 // The relevance of past searches falls off over time. There are two distinct
1101 // equations used. If the first equation is used (searches to the primary
1102 // provider that we want to score aggressively), the score is in the range
1103 // 1300-1599 (unless |prevent_search_history_inlining|, in which case
1104 // it's in the range 1200-1299). If the second equation is used the
1105 // relevance of a search 15 minutes ago is discounted 50 points, while the
1106 // relevance of a search two weeks ago is discounted 450 points.
1107 double elapsed_time = std::max((base::Time::Now() - time).InSecondsF(), 0.0);
1108 bool is_primary_provider = is_keyword || !providers_.has_keyword_provider();
1109 if (is_primary_provider && use_aggressive_method) {
1110 // Searches with the past two days get a different curve.
1111 const double autocomplete_time = 2 * 24 * 60 * 60;
1112 if (elapsed_time < autocomplete_time) {
1113 int max_score = is_keyword ? 1599 : 1399;
1114 if (prevent_search_history_inlining)
1115 max_score = 1299;
1116 return max_score - static_cast<int>(99 *
1117 std::pow(elapsed_time / autocomplete_time, 2.5));
1119 elapsed_time -= autocomplete_time;
1122 const int score_discount =
1123 static_cast<int>(6.5 * std::pow(elapsed_time, 0.3));
1125 // Don't let scores go below 0. Negative relevance scores are meaningful in
1126 // a different way.
1127 int base_score;
1128 if (is_primary_provider)
1129 base_score = (input_.type() == metrics::OmniboxInputType::URL) ? 750 : 1050;
1130 else
1131 base_score = 200;
1132 return std::max(0, base_score - score_discount);
1135 AutocompleteMatch SearchProvider::NavigationToMatch(
1136 const SearchSuggestionParser::NavigationResult& navigation) {
1137 base::string16 input;
1138 const bool trimmed_whitespace = base::TrimWhitespace(
1139 navigation.from_keyword_provider() ?
1140 keyword_input_.text() : input_.text(),
1141 base::TRIM_TRAILING, &input) != base::TRIM_NONE;
1142 AutocompleteMatch match(this, navigation.relevance(), false,
1143 navigation.type());
1144 match.destination_url = navigation.url();
1145 BaseSearchProvider::SetDeletionURL(navigation.deletion_url(), &match);
1146 // First look for the user's input inside the formatted url as it would be
1147 // without trimming the scheme, so we can find matches at the beginning of the
1148 // scheme.
1149 const URLPrefix* prefix =
1150 URLPrefix::BestURLPrefix(navigation.formatted_url(), input);
1151 size_t match_start = (prefix == NULL) ?
1152 navigation.formatted_url().find(input) : prefix->prefix.length();
1153 bool trim_http = !AutocompleteInput::HasHTTPScheme(input) &&
1154 (!prefix || (match_start != 0));
1155 const net::FormatUrlTypes format_types =
1156 net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP);
1158 const std::string languages(client_->AcceptLanguages());
1159 size_t inline_autocomplete_offset = (prefix == NULL) ?
1160 base::string16::npos : (match_start + input.length());
1161 match.fill_into_edit +=
1162 AutocompleteInput::FormattedStringWithEquivalentMeaning(
1163 navigation.url(),
1164 net::FormatUrl(navigation.url(), languages, format_types,
1165 net::UnescapeRule::SPACES, NULL, NULL,
1166 &inline_autocomplete_offset),
1167 client_->SchemeClassifier());
1168 // Preserve the forced query '?' prefix in |match.fill_into_edit|.
1169 // Otherwise, user edits to a suggestion would show non-Search results.
1170 if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) {
1171 match.fill_into_edit.insert(0, base::ASCIIToUTF16("?"));
1172 if (inline_autocomplete_offset != base::string16::npos)
1173 ++inline_autocomplete_offset;
1175 if (inline_autocomplete_offset != base::string16::npos) {
1176 DCHECK(inline_autocomplete_offset <= match.fill_into_edit.length());
1177 match.inline_autocompletion =
1178 match.fill_into_edit.substr(inline_autocomplete_offset);
1180 // An inlineable navsuggestion can only be the default match when there
1181 // is no keyword provider active, lest it appear first and break the user
1182 // out of keyword mode. It can also only be default if either the inline
1183 // autocompletion is empty or we're not preventing inline autocompletion.
1184 // Finally, if we have an inlineable navsuggestion with an inline completion
1185 // that we're not preventing, make sure we didn't trim any whitespace.
1186 // We don't want to claim http://foo.com/bar is inlineable against the
1187 // input "foo.com/b ".
1188 match.allowed_to_be_default_match = (prefix != NULL) &&
1189 (providers_.GetKeywordProviderURL() == NULL) &&
1190 (match.inline_autocompletion.empty() ||
1191 (!input_.prevent_inline_autocomplete() && !trimmed_whitespace));
1192 match.EnsureUWYTIsAllowedToBeDefault(
1193 input_.canonicalized_url(), providers_.template_url_service());
1195 match.contents = navigation.match_contents();
1196 match.contents_class = navigation.match_contents_class();
1197 match.description = navigation.description();
1198 AutocompleteMatch::ClassifyMatchInString(input, match.description,
1199 ACMatchClassification::NONE, &match.description_class);
1201 match.RecordAdditionalInfo(
1202 kRelevanceFromServerKey,
1203 navigation.relevance_from_server() ? kTrue : kFalse);
1204 match.RecordAdditionalInfo(kShouldPrefetchKey, kFalse);
1206 return match;
1209 void SearchProvider::UpdateDone() {
1210 // We're done when the timer isn't running, there are no suggest queries
1211 // pending, and we're not waiting on Instant.
1212 done_ = !timer_.IsRunning() && (suggest_results_pending_ == 0);
1215 std::string SearchProvider::GetSessionToken() {
1216 base::TimeTicks current_time(base::TimeTicks::Now());
1217 // Renew token if it expired.
1218 if (current_time > token_expiration_time_) {
1219 const size_t kTokenBytes = 12;
1220 std::string raw_data;
1221 base::RandBytes(WriteInto(&raw_data, kTokenBytes + 1), kTokenBytes);
1222 base::Base64Encode(raw_data, &current_token_);
1224 // Make the base64 encoded value URL and filename safe(see RFC 3548).
1225 std::replace(current_token_.begin(), current_token_.end(), '+', '-');
1226 std::replace(current_token_.begin(), current_token_.end(), '/', '_');
1229 // Extend expiration time another 60 seconds.
1230 token_expiration_time_ = current_time + base::TimeDelta::FromSeconds(60);
1232 return current_token_;
1235 void SearchProvider::RegisterDisplayedAnswers(
1236 const AutocompleteResult& result) {
1237 if (result.empty())
1238 return;
1240 // The answer must be in the first or second slot to be considered. It should
1241 // only be in the second slot if AutocompleteController ranked a local search
1242 // history or a verbatim item higher than the answer.
1243 AutocompleteResult::const_iterator match = result.begin();
1244 if (match->answer_contents.empty() && result.size() > 1)
1245 ++match;
1246 if (match->answer_contents.empty() || match->answer_type.empty() ||
1247 match->fill_into_edit.empty())
1248 return;
1250 // Valid answer encountered, cache it for further queries.
1251 answers_cache_.UpdateRecentAnswers(match->fill_into_edit, match->answer_type);
1254 void SearchProvider::DoAnswersQuery(const AutocompleteInput& input) {
1255 prefetch_data_ = answers_cache_.GetTopAnswerEntry(input.text());