93cb059b5db8ece6c964d2529bab7cef101d45ad
[platform/framework/web/crosswalk.git] / src / components / omnibox / search_provider.cc
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.
4
5 #include "components/omnibox/search_provider.h"
6
7 #include <algorithm>
8 #include <cmath>
9
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/suggestion_answer.h"
29 #include "components/omnibox/url_prefix.h"
30 #include "components/search/search.h"
31 #include "components/search_engines/template_url_prepopulate_data.h"
32 #include "components/search_engines/template_url_service.h"
33 #include "components/variations/net/variations_http_header_provider.h"
34 #include "grit/components_strings.h"
35 #include "net/base/escape.h"
36 #include "net/base/load_flags.h"
37 #include "net/base/net_util.h"
38 #include "net/http/http_request_headers.h"
39 #include "net/url_request/url_fetcher.h"
40 #include "net/url_request/url_request_status.h"
41 #include "ui/base/l10n/l10n_util.h"
42 #include "url/url_constants.h"
43 #include "url/url_util.h"
44
45 // Helpers --------------------------------------------------------------------
46
47 namespace {
48
49 // We keep track in a histogram how many suggest requests we send, how
50 // many suggest requests we invalidate (e.g., due to a user typing
51 // another character), and how many replies we receive.
52 // *** ADD NEW ENUMS AFTER ALL PREVIOUSLY DEFINED ONES! ***
53 //     (excluding the end-of-list enum value)
54 // We do not want values of existing enums to change or else it screws
55 // up the statistics.
56 enum SuggestRequestsHistogramValue {
57   REQUEST_SENT = 1,
58   REQUEST_INVALIDATED,
59   REPLY_RECEIVED,
60   MAX_SUGGEST_REQUEST_HISTOGRAM_VALUE
61 };
62
63 // The verbatim score for an input which is not an URL.
64 const int kNonURLVerbatimRelevance = 1300;
65
66 // Increments the appropriate value in the histogram by one.
67 void LogOmniboxSuggestRequest(
68     SuggestRequestsHistogramValue request_value) {
69   UMA_HISTOGRAM_ENUMERATION("Omnibox.SuggestRequests", request_value,
70                             MAX_SUGGEST_REQUEST_HISTOGRAM_VALUE);
71 }
72
73 bool HasMultipleWords(const base::string16& text) {
74   base::i18n::BreakIterator i(text, base::i18n::BreakIterator::BREAK_WORD);
75   bool found_word = false;
76   if (i.Init()) {
77     while (i.Advance()) {
78       if (i.IsWord()) {
79         if (found_word)
80           return true;
81         found_word = true;
82       }
83     }
84   }
85   return false;
86 }
87
88 }  // namespace
89
90 // SearchProvider::Providers --------------------------------------------------
91
92 SearchProvider::Providers::Providers(TemplateURLService* template_url_service)
93     : template_url_service_(template_url_service) {}
94
95 const TemplateURL* SearchProvider::Providers::GetDefaultProviderURL() const {
96   return default_provider_.empty() ? NULL :
97       template_url_service_->GetTemplateURLForKeyword(default_provider_);
98 }
99
100 const TemplateURL* SearchProvider::Providers::GetKeywordProviderURL() const {
101   return keyword_provider_.empty() ? NULL :
102       template_url_service_->GetTemplateURLForKeyword(keyword_provider_);
103 }
104
105
106 // SearchProvider::CompareScoredResults ---------------------------------------
107
108 class SearchProvider::CompareScoredResults {
109  public:
110   bool operator()(const SearchSuggestionParser::Result& a,
111                   const SearchSuggestionParser::Result& b) {
112     // Sort in descending relevance order.
113     return a.relevance() > b.relevance();
114   }
115 };
116
117
118 // SearchProvider -------------------------------------------------------------
119
120 SearchProvider::SearchProvider(
121     AutocompleteProviderListener* listener,
122     TemplateURLService* template_url_service,
123     scoped_ptr<AutocompleteProviderClient> client)
124     : BaseSearchProvider(template_url_service, client.Pass(),
125                          AutocompleteProvider::TYPE_SEARCH),
126       listener_(listener),
127       suggest_results_pending_(0),
128       providers_(template_url_service),
129       answers_cache_(10) {
130 }
131
132 // static
133 std::string SearchProvider::GetSuggestMetadata(const AutocompleteMatch& match) {
134   return match.GetAdditionalInfo(kSuggestMetadataKey);
135 }
136
137 void SearchProvider::ResetSession() {
138   field_trial_triggered_in_session_ = false;
139 }
140
141 SearchProvider::~SearchProvider() {
142 }
143
144 // static
145 int SearchProvider::CalculateRelevanceForKeywordVerbatim(
146     metrics::OmniboxInputType::Type type,
147     bool prefer_keyword) {
148   // This function is responsible for scoring verbatim query matches
149   // for non-extension keywords.  KeywordProvider::CalculateRelevance()
150   // scores verbatim query matches for extension keywords, as well as
151   // for keyword matches (i.e., suggestions of a keyword itself, not a
152   // suggestion of a query on a keyword search engine).  These two
153   // functions are currently in sync, but there's no reason we
154   // couldn't decide in the future to score verbatim matches
155   // differently for extension and non-extension keywords.  If you
156   // make such a change, however, you should update this comment to
157   // describe it, so it's clear why the functions diverge.
158   if (prefer_keyword)
159     return 1500;
160   return (type == metrics::OmniboxInputType::QUERY) ? 1450 : 1100;
161 }
162
163 // static
164 void SearchProvider::UpdateOldResults(
165     bool minimal_changes,
166     SearchSuggestionParser::Results* results) {
167   // When called without |minimal_changes|, it likely means the user has
168   // pressed a key.  Revise the cached results appropriately.
169   if (!minimal_changes) {
170     for (SearchSuggestionParser::SuggestResults::iterator sug_it =
171              results->suggest_results.begin();
172          sug_it != results->suggest_results.end(); ++sug_it) {
173       sug_it->set_received_after_last_keystroke(false);
174     }
175     for (SearchSuggestionParser::NavigationResults::iterator nav_it =
176              results->navigation_results.begin();
177          nav_it != results->navigation_results.end(); ++nav_it) {
178       nav_it->set_received_after_last_keystroke(false);
179     }
180   }
181 }
182
183 // static
184 ACMatches::iterator SearchProvider::FindTopMatch(ACMatches* matches) {
185   ACMatches::iterator it = matches->begin();
186   while ((it != matches->end()) && !it->allowed_to_be_default_match)
187     ++it;
188   return it;
189 }
190
191 void SearchProvider::Start(const AutocompleteInput& input,
192                            bool minimal_changes) {
193   // Do our best to load the model as early as possible.  This will reduce
194   // odds of having the model not ready when really needed (a non-empty input).
195   TemplateURLService* model = providers_.template_url_service();
196   DCHECK(model);
197   model->Load();
198
199   matches_.clear();
200   field_trial_triggered_ = false;
201
202   // Can't return search/suggest results for bogus input.
203   if (input.type() == metrics::OmniboxInputType::INVALID) {
204     Stop(true);
205     return;
206   }
207
208   keyword_input_ = input;
209   const TemplateURL* keyword_provider =
210       KeywordProvider::GetSubstitutingTemplateURLForInput(model,
211                                                           &keyword_input_);
212   if (keyword_provider == NULL)
213     keyword_input_.Clear();
214   else if (keyword_input_.text().empty())
215     keyword_provider = NULL;
216
217   const TemplateURL* default_provider = model->GetDefaultSearchProvider();
218   if (default_provider &&
219       !default_provider->SupportsReplacement(model->search_terms_data()))
220     default_provider = NULL;
221
222   if (keyword_provider == default_provider)
223     default_provider = NULL;  // No use in querying the same provider twice.
224
225   if (!default_provider && !keyword_provider) {
226     // No valid providers.
227     Stop(true);
228     return;
229   }
230
231   // If we're still running an old query but have since changed the query text
232   // or the providers, abort the query.
233   base::string16 default_provider_keyword(default_provider ?
234       default_provider->keyword() : base::string16());
235   base::string16 keyword_provider_keyword(keyword_provider ?
236       keyword_provider->keyword() : base::string16());
237   if (!minimal_changes ||
238       !providers_.equal(default_provider_keyword, keyword_provider_keyword)) {
239     // Cancel any in-flight suggest requests.
240     if (!done_)
241       Stop(false);
242   }
243
244   providers_.set(default_provider_keyword, keyword_provider_keyword);
245
246   if (input.text().empty()) {
247     // User typed "?" alone.  Give them a placeholder result indicating what
248     // this syntax does.
249     if (default_provider) {
250       AutocompleteMatch match;
251       match.provider = this;
252       match.contents.assign(l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE));
253       match.contents_class.push_back(
254           ACMatchClassification(0, ACMatchClassification::NONE));
255       match.keyword = providers_.default_provider();
256       match.allowed_to_be_default_match = true;
257       matches_.push_back(match);
258     }
259     Stop(true);
260     return;
261   }
262
263   input_ = input;
264
265   DoHistoryQuery(minimal_changes);
266   // Answers needs scored history results before any suggest query has been
267   // started, since the query for answer-bearing results needs additional
268   // prefetch information based on the highest-scored local history result.
269   if (OmniboxFieldTrial::EnableAnswersInSuggest()) {
270     ScoreHistoryResults(raw_default_history_results_,
271                         false,
272                         &transformed_default_history_results_);
273     ScoreHistoryResults(raw_keyword_history_results_,
274                         true,
275                         &transformed_keyword_history_results_);
276     prefetch_data_ = FindAnswersPrefetchData();
277
278     // Raw results are not needed any more.
279     raw_default_history_results_.clear();
280     raw_keyword_history_results_.clear();
281   } else {
282     transformed_default_history_results_.clear();
283     transformed_keyword_history_results_.clear();
284   }
285
286   StartOrStopSuggestQuery(minimal_changes);
287   UpdateMatches();
288 }
289
290 void SearchProvider::Stop(bool clear_cached_results) {
291   StopSuggest();
292   done_ = true;
293
294   if (clear_cached_results)
295     ClearAllResults();
296 }
297
298 const TemplateURL* SearchProvider::GetTemplateURL(bool is_keyword) const {
299   return is_keyword ? providers_.GetKeywordProviderURL()
300                     : providers_.GetDefaultProviderURL();
301 }
302
303 const AutocompleteInput SearchProvider::GetInput(bool is_keyword) const {
304   return is_keyword ? keyword_input_ : input_;
305 }
306
307 bool SearchProvider::ShouldAppendExtraParams(
308     const SearchSuggestionParser::SuggestResult& result) const {
309   return !result.from_keyword_provider() ||
310       providers_.default_provider().empty();
311 }
312
313 void SearchProvider::RecordDeletionResult(bool success) {
314   if (success) {
315     base::RecordAction(
316         base::UserMetricsAction("Omnibox.ServerSuggestDelete.Success"));
317   } else {
318     base::RecordAction(
319         base::UserMetricsAction("Omnibox.ServerSuggestDelete.Failure"));
320   }
321 }
322
323 void SearchProvider::OnURLFetchComplete(const net::URLFetcher* source) {
324   DCHECK(!done_);
325   --suggest_results_pending_;
326   DCHECK_GE(suggest_results_pending_, 0);  // Should never go negative.
327
328   const bool is_keyword = source == keyword_fetcher_.get();
329
330   // Ensure the request succeeded and that the provider used is still available.
331   // A verbatim match cannot be generated without this provider, causing errors.
332   const bool request_succeeded =
333       source->GetStatus().is_success() && (source->GetResponseCode() == 200) &&
334       GetTemplateURL(is_keyword);
335
336   LogFetchComplete(request_succeeded, is_keyword);
337
338   bool results_updated = false;
339   if (request_succeeded) {
340     scoped_ptr<base::Value> data(SearchSuggestionParser::DeserializeJsonData(
341         SearchSuggestionParser::ExtractJsonData(source)));
342     if (data) {
343       SearchSuggestionParser::Results* results =
344           is_keyword ? &keyword_results_ : &default_results_;
345       results_updated = ParseSuggestResults(*data, -1, is_keyword, results);
346       if (results_updated)
347         SortResults(is_keyword, results);
348     }
349   }
350   UpdateMatches();
351   if (done_ || results_updated)
352     listener_->OnProviderUpdate(results_updated);
353 }
354
355 void SearchProvider::StopSuggest() {
356   // Increment the appropriate field in the histogram by the number of
357   // pending requests that were invalidated.
358   for (int i = 0; i < suggest_results_pending_; ++i)
359     LogOmniboxSuggestRequest(REQUEST_INVALIDATED);
360   suggest_results_pending_ = 0;
361   timer_.Stop();
362   // Stop any in-progress URL fetches.
363   keyword_fetcher_.reset();
364   default_fetcher_.reset();
365 }
366
367 void SearchProvider::ClearAllResults() {
368   keyword_results_.Clear();
369   default_results_.Clear();
370 }
371
372 void SearchProvider::UpdateMatchContentsClass(
373     const base::string16& input_text,
374     SearchSuggestionParser::Results* results) {
375   for (SearchSuggestionParser::SuggestResults::iterator sug_it =
376            results->suggest_results.begin();
377        sug_it != results->suggest_results.end(); ++sug_it) {
378     sug_it->ClassifyMatchContents(false, input_text);
379   }
380   const std::string languages(client_->AcceptLanguages());
381   for (SearchSuggestionParser::NavigationResults::iterator nav_it =
382            results->navigation_results.begin();
383        nav_it != results->navigation_results.end(); ++nav_it) {
384     nav_it->CalculateAndClassifyMatchContents(false, input_text, languages);
385   }
386 }
387
388 void SearchProvider::SortResults(bool is_keyword,
389                                  SearchSuggestionParser::Results* results) {
390   // Ignore suggested scores for non-keyword matches in keyword mode; if the
391   // server is allowed to score these, it could interfere with the user's
392   // ability to get good keyword results.
393   const bool abandon_suggested_scores =
394       !is_keyword && !providers_.keyword_provider().empty();
395   // Apply calculated relevance scores to suggestions if valid relevances were
396   // not provided or we're abandoning suggested scores entirely.
397   if (!results->relevances_from_server || abandon_suggested_scores) {
398     ApplyCalculatedSuggestRelevance(&results->suggest_results);
399     ApplyCalculatedNavigationRelevance(&results->navigation_results);
400     // If abandoning scores entirely, also abandon the verbatim score.
401     if (abandon_suggested_scores)
402       results->verbatim_relevance = -1;
403   }
404
405   // Keep the result lists sorted.
406   const CompareScoredResults comparator = CompareScoredResults();
407   std::stable_sort(results->suggest_results.begin(),
408                    results->suggest_results.end(),
409                    comparator);
410   std::stable_sort(results->navigation_results.begin(),
411                    results->navigation_results.end(),
412                    comparator);
413 }
414
415 void SearchProvider::LogFetchComplete(bool success, bool is_keyword) {
416   LogOmniboxSuggestRequest(REPLY_RECEIVED);
417   // Record response time for suggest requests sent to Google.  We care
418   // only about the common case: the Google default provider used in
419   // non-keyword mode.
420   const TemplateURL* default_url = providers_.GetDefaultProviderURL();
421   if (!is_keyword && default_url &&
422       (TemplateURLPrepopulateData::GetEngineType(
423           *default_url,
424           providers_.template_url_service()->search_terms_data()) ==
425        SEARCH_ENGINE_GOOGLE)) {
426     const base::TimeDelta elapsed_time =
427         base::TimeTicks::Now() - time_suggest_request_sent_;
428     if (success) {
429       UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Success.GoogleResponseTime",
430                           elapsed_time);
431     } else {
432       UMA_HISTOGRAM_TIMES("Omnibox.SuggestRequest.Failure.GoogleResponseTime",
433                           elapsed_time);
434     }
435   }
436 }
437
438 void SearchProvider::UpdateMatches() {
439   PersistTopSuggestions(&default_results_);
440   PersistTopSuggestions(&keyword_results_);
441   ConvertResultsToAutocompleteMatches();
442
443   // Check constraints that may be violated by suggested relevances.
444   if (!matches_.empty() &&
445       (default_results_.HasServerProvidedScores() ||
446        keyword_results_.HasServerProvidedScores())) {
447     // These blocks attempt to repair undesirable behavior by suggested
448     // relevances with minimal impact, preserving other suggested relevances.
449
450     const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
451     const bool is_extension_keyword = (keyword_url != NULL) &&
452         (keyword_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION);
453     if ((keyword_url != NULL) && !is_extension_keyword &&
454         (FindTopMatch() == matches_.end())) {
455       // In non-extension keyword mode, disregard the keyword verbatim suggested
456       // relevance if necessary, so at least one match is allowed to be default.
457       // (In extension keyword mode this is not necessary because the extension
458       // will return a default match.)  Give keyword verbatim the lowest
459       // non-zero score to best reflect what the server desired.
460       DCHECK_EQ(0, keyword_results_.verbatim_relevance);
461       keyword_results_.verbatim_relevance = 1;
462       ConvertResultsToAutocompleteMatches();
463     }
464     if (IsTopMatchSearchWithURLInput()) {
465       // Disregard the suggested search and verbatim relevances if the input
466       // type is URL and the top match is a highly-ranked search suggestion.
467       // For example, prevent a search for "foo.com" from outranking another
468       // provider's navigation for "foo.com" or "foo.com/url_from_history".
469       ApplyCalculatedSuggestRelevance(&keyword_results_.suggest_results);
470       ApplyCalculatedSuggestRelevance(&default_results_.suggest_results);
471       default_results_.verbatim_relevance = -1;
472       keyword_results_.verbatim_relevance = -1;
473       ConvertResultsToAutocompleteMatches();
474     }
475     if (!is_extension_keyword && (FindTopMatch() == matches_.end())) {
476       // Guarantee that SearchProvider returns a legal default match (except
477       // when in extension-based keyword mode).  The omnibox always needs at
478       // least one legal default match, and it relies on SearchProvider in
479       // combination with KeywordProvider (for extension-based keywords) to
480       // always return one.  Give the verbatim suggestion the lowest non-zero
481       // scores to best reflect what the server desired.
482       DCHECK_EQ(0, default_results_.verbatim_relevance);
483       default_results_.verbatim_relevance = 1;
484       // We do not have to alter keyword_results_.verbatim_relevance here.
485       // If the user is in keyword mode, we already reverted (earlier in this
486       // function) the instructions to suppress keyword verbatim.
487       ConvertResultsToAutocompleteMatches();
488     }
489     DCHECK(!IsTopMatchSearchWithURLInput());
490     DCHECK(is_extension_keyword || (FindTopMatch() != matches_.end()));
491   }
492   UMA_HISTOGRAM_CUSTOM_COUNTS(
493       "Omnibox.SearchProviderMatches", matches_.size(), 1, 6, 7);
494
495   // Record the top suggestion (if any) for future use.
496   top_query_suggestion_match_contents_ = base::string16();
497   top_navigation_suggestion_ = GURL();
498   ACMatches::const_iterator first_match = FindTopMatch();
499   if ((first_match != matches_.end()) &&
500       !first_match->inline_autocompletion.empty()) {
501     // Identify if this match came from a query suggestion or a navsuggestion.
502     // In either case, extracts the identifying feature of the suggestion
503     // (query string or navigation url).
504     if (AutocompleteMatch::IsSearchType(first_match->type))
505       top_query_suggestion_match_contents_ = first_match->contents;
506     else
507       top_navigation_suggestion_ = first_match->destination_url;
508   }
509
510   UpdateDone();
511 }
512
513 void SearchProvider::Run() {
514   // Start a new request with the current input.
515   suggest_results_pending_ = 0;
516   time_suggest_request_sent_ = base::TimeTicks::Now();
517
518   default_fetcher_.reset(CreateSuggestFetcher(kDefaultProviderURLFetcherID,
519       providers_.GetDefaultProviderURL(), input_));
520   keyword_fetcher_.reset(CreateSuggestFetcher(kKeywordProviderURLFetcherID,
521       providers_.GetKeywordProviderURL(), keyword_input_));
522
523   // Both the above can fail if the providers have been modified or deleted
524   // since the query began.
525   if (suggest_results_pending_ == 0) {
526     UpdateDone();
527     // We only need to update the listener if we're actually done.
528     if (done_)
529       listener_->OnProviderUpdate(false);
530   }
531 }
532
533 void SearchProvider::DoHistoryQuery(bool minimal_changes) {
534   // The history query results are synchronous, so if minimal_changes is true,
535   // we still have the last results and don't need to do anything.
536   if (minimal_changes)
537     return;
538
539   raw_keyword_history_results_.clear();
540   raw_default_history_results_.clear();
541
542   if (OmniboxFieldTrial::SearchHistoryDisable(
543       input_.current_page_classification()))
544     return;
545
546   history::URLDatabase* url_db = client_->InMemoryDatabase();
547   if (!url_db)
548     return;
549
550   // Request history for both the keyword and default provider.  We grab many
551   // more matches than we'll ultimately clamp to so that if there are several
552   // recent multi-word matches who scores are lowered (see
553   // ScoreHistoryResults()), they won't crowd out older, higher-scoring
554   // matches.  Note that this doesn't fix the problem entirely, but merely
555   // limits it to cases with a very large number of such multi-word matches; for
556   // now, this seems OK compared with the complexity of a real fix, which would
557   // require multiple searches and tracking of "single- vs. multi-word" in the
558   // database.
559   int num_matches = kMaxMatches * 5;
560   const TemplateURL* default_url = providers_.GetDefaultProviderURL();
561   if (default_url) {
562     const base::TimeTicks start_time = base::TimeTicks::Now();
563     url_db->GetMostRecentKeywordSearchTerms(default_url->id(),
564                                             input_.text(),
565                                             num_matches,
566                                             &raw_default_history_results_);
567     UMA_HISTOGRAM_TIMES(
568         "Omnibox.SearchProvider.GetMostRecentKeywordTermsDefaultProviderTime",
569         base::TimeTicks::Now() - start_time);
570   }
571   const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
572   if (keyword_url) {
573     url_db->GetMostRecentKeywordSearchTerms(keyword_url->id(),
574                                             keyword_input_.text(),
575                                             num_matches,
576                                             &raw_keyword_history_results_);
577   }
578 }
579
580 base::TimeDelta SearchProvider::GetSuggestQueryDelay() const {
581   bool from_last_keystroke;
582   int polling_delay_ms;
583   OmniboxFieldTrial::GetSuggestPollingStrategy(&from_last_keystroke,
584                                                &polling_delay_ms);
585
586   base::TimeDelta delay(base::TimeDelta::FromMilliseconds(polling_delay_ms));
587   if (from_last_keystroke)
588     return delay;
589
590   base::TimeDelta time_since_last_suggest_request =
591       base::TimeTicks::Now() - time_suggest_request_sent_;
592   return std::max(base::TimeDelta(), delay - time_since_last_suggest_request);
593 }
594
595 void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) {
596   if (!IsQuerySuitableForSuggest()) {
597     StopSuggest();
598     ClearAllResults();
599     return;
600   }
601
602   if (OmniboxFieldTrial::DisableResultsCaching())
603     ClearAllResults();
604
605   // For the minimal_changes case, if we finished the previous query and still
606   // have its results, or are allowed to keep running it, just do that, rather
607   // than starting a new query.
608   if (minimal_changes &&
609       (!default_results_.suggest_results.empty() ||
610        !default_results_.navigation_results.empty() ||
611        !keyword_results_.suggest_results.empty() ||
612        !keyword_results_.navigation_results.empty() ||
613        (!done_ && input_.want_asynchronous_matches())))
614     return;
615
616   // We can't keep running any previous query, so halt it.
617   StopSuggest();
618
619   UpdateAllOldResults(minimal_changes);
620
621   // Update the content classifications of remaining results so they look good
622   // against the current input.
623   UpdateMatchContentsClass(input_.text(), &default_results_);
624   if (!keyword_input_.text().empty())
625     UpdateMatchContentsClass(keyword_input_.text(), &keyword_results_);
626
627   // We can't start a new query if we're only allowed synchronous results.
628   if (!input_.want_asynchronous_matches())
629     return;
630
631   // Kick off a timer that will start the URL fetch if it completes before
632   // the user types another character.  Requests may be delayed to avoid
633   // flooding the server with requests that are likely to be thrown away later
634   // anyway.
635   const base::TimeDelta delay = GetSuggestQueryDelay();
636   if (delay <= base::TimeDelta()) {
637     Run();
638     return;
639   }
640   timer_.Start(FROM_HERE, delay, this, &SearchProvider::Run);
641 }
642
643 bool SearchProvider::IsQuerySuitableForSuggest() const {
644   // Don't run Suggest in incognito mode, if the engine doesn't support it, or
645   // if the user has disabled it.
646   const TemplateURL* default_url = providers_.GetDefaultProviderURL();
647   const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
648   if (client_->IsOffTheRecord() ||
649       ((!default_url || default_url->suggestions_url().empty()) &&
650        (!keyword_url || keyword_url->suggestions_url().empty())) ||
651       !client_->SearchSuggestEnabled())
652     return false;
653
654   // If the input type might be a URL, we take extra care so that private data
655   // isn't sent to the server.
656
657   // FORCED_QUERY means the user is explicitly asking us to search for this, so
658   // we assume it isn't a URL and/or there isn't private data.
659   if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY)
660     return true;
661
662   // Next we check the scheme.  If this is UNKNOWN/URL with a scheme that isn't
663   // http/https/ftp, we shouldn't send it.  Sending things like file: and data:
664   // is both a waste of time and a disclosure of potentially private, local
665   // data.  Other "schemes" may actually be usernames, and we don't want to send
666   // passwords.  If the scheme is OK, we still need to check other cases below.
667   // If this is QUERY, then the presence of these schemes means the user
668   // explicitly typed one, and thus this is probably a URL that's being entered
669   // and happens to currently be invalid -- in which case we again want to run
670   // our checks below.  Other QUERY cases are less likely to be URLs and thus we
671   // assume we're OK.
672   if (!LowerCaseEqualsASCII(input_.scheme(), url::kHttpScheme) &&
673       !LowerCaseEqualsASCII(input_.scheme(), url::kHttpsScheme) &&
674       !LowerCaseEqualsASCII(input_.scheme(), url::kFtpScheme))
675     return (input_.type() == metrics::OmniboxInputType::QUERY);
676
677   // Don't send URLs with usernames, queries or refs.  Some of these are
678   // private, and the Suggest server is unlikely to have any useful results
679   // for any of them.  Also don't send URLs with ports, as we may initially
680   // think that a username + password is a host + port (and we don't want to
681   // send usernames/passwords), and even if the port really is a port, the
682   // server is once again unlikely to have and useful results.
683   // Note that we only block based on refs if the input is URL-typed, as search
684   // queries can legitimately have #s in them which the URL parser
685   // overaggressively categorizes as a url with a ref.
686   const url::Parsed& parts = input_.parts();
687   if (parts.username.is_nonempty() || parts.port.is_nonempty() ||
688       parts.query.is_nonempty() ||
689       (parts.ref.is_nonempty() &&
690        (input_.type() == metrics::OmniboxInputType::URL)))
691     return false;
692
693   // Don't send anything for https except the hostname.  Hostnames are OK
694   // because they are visible when the TCP connection is established, but the
695   // specific path may reveal private information.
696   if (LowerCaseEqualsASCII(input_.scheme(), url::kHttpsScheme) &&
697       parts.path.is_nonempty())
698     return false;
699
700   return true;
701 }
702
703 void SearchProvider::UpdateAllOldResults(bool minimal_changes) {
704   if (keyword_input_.text().empty()) {
705     // User is either in keyword mode with a blank input or out of
706     // keyword mode entirely.
707     keyword_results_.Clear();
708   }
709   UpdateOldResults(minimal_changes, &default_results_);
710   UpdateOldResults(minimal_changes, &keyword_results_);
711 }
712
713 void SearchProvider::PersistTopSuggestions(
714     SearchSuggestionParser::Results* results) {
715   // Mark any results matching the current top results as having been received
716   // prior to the last keystroke.  That prevents asynchronous updates from
717   // clobbering top results, which may be used for inline autocompletion.
718   // Other results don't need similar changes, because they shouldn't be
719   // displayed asynchronously anyway.
720   if (!top_query_suggestion_match_contents_.empty()) {
721     for (SearchSuggestionParser::SuggestResults::iterator sug_it =
722              results->suggest_results.begin();
723          sug_it != results->suggest_results.end(); ++sug_it) {
724       if (sug_it->match_contents() == top_query_suggestion_match_contents_)
725         sug_it->set_received_after_last_keystroke(false);
726     }
727   }
728   if (top_navigation_suggestion_.is_valid()) {
729     for (SearchSuggestionParser::NavigationResults::iterator nav_it =
730              results->navigation_results.begin();
731          nav_it != results->navigation_results.end(); ++nav_it) {
732       if (nav_it->url() == top_navigation_suggestion_)
733         nav_it->set_received_after_last_keystroke(false);
734     }
735   }
736 }
737
738 void SearchProvider::ApplyCalculatedSuggestRelevance(
739     SearchSuggestionParser::SuggestResults* list) {
740   for (size_t i = 0; i < list->size(); ++i) {
741     SearchSuggestionParser::SuggestResult& result = (*list)[i];
742     result.set_relevance(
743         result.CalculateRelevance(input_, providers_.has_keyword_provider()) +
744         (list->size() - i - 1));
745     result.set_relevance_from_server(false);
746   }
747 }
748
749 void SearchProvider::ApplyCalculatedNavigationRelevance(
750     SearchSuggestionParser::NavigationResults* list) {
751   for (size_t i = 0; i < list->size(); ++i) {
752     SearchSuggestionParser::NavigationResult& result = (*list)[i];
753     result.set_relevance(
754         result.CalculateRelevance(input_, providers_.has_keyword_provider()) +
755         (list->size() - i - 1));
756     result.set_relevance_from_server(false);
757   }
758 }
759
760 net::URLFetcher* SearchProvider::CreateSuggestFetcher(
761     int id,
762     const TemplateURL* template_url,
763     const AutocompleteInput& input) {
764   if (!template_url || template_url->suggestions_url().empty())
765     return NULL;
766
767   // Bail if the suggestion URL is invalid with the given replacements.
768   TemplateURLRef::SearchTermsArgs search_term_args(input.text());
769   search_term_args.input_type = input.type();
770   search_term_args.cursor_position = input.cursor_position();
771   search_term_args.page_classification = input.current_page_classification();
772   if (OmniboxFieldTrial::EnableAnswersInSuggest()) {
773     search_term_args.session_token = GetSessionToken();
774     if (!prefetch_data_.full_query_text.empty()) {
775       search_term_args.prefetch_query =
776           base::UTF16ToUTF8(prefetch_data_.full_query_text);
777       search_term_args.prefetch_query_type =
778           base::UTF16ToUTF8(prefetch_data_.query_type);
779     }
780   }
781   GURL suggest_url(template_url->suggestions_url_ref().ReplaceSearchTerms(
782       search_term_args,
783       providers_.template_url_service()->search_terms_data()));
784   if (!suggest_url.is_valid())
785     return NULL;
786   // Send the current page URL if user setting and URL requirements are met and
787   // the user is in the field trial.
788   if (CanSendURL(current_page_url_, suggest_url, template_url,
789                  input.current_page_classification(),
790                  template_url_service_->search_terms_data(), client_.get()) &&
791       OmniboxFieldTrial::InZeroSuggestAfterTypingFieldTrial()) {
792     search_term_args.current_page_url = current_page_url_.spec();
793     // Create the suggest URL again with the current page URL.
794     suggest_url = GURL(template_url->suggestions_url_ref().ReplaceSearchTerms(
795         search_term_args,
796         providers_.template_url_service()->search_terms_data()));
797   }
798
799   suggest_results_pending_++;
800   LogOmniboxSuggestRequest(REQUEST_SENT);
801
802   net::URLFetcher* fetcher =
803       net::URLFetcher::Create(id, suggest_url, net::URLFetcher::GET, this);
804   fetcher->SetRequestContext(client_->RequestContext());
805   fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
806   // Add Chrome experiment state to the request headers.
807   net::HttpRequestHeaders headers;
808   variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders(
809       fetcher->GetOriginalURL(), client_->IsOffTheRecord(), false, &headers);
810   fetcher->SetExtraRequestHeaders(headers.ToString());
811   fetcher->Start();
812   return fetcher;
813 }
814
815 void SearchProvider::ConvertResultsToAutocompleteMatches() {
816   // Convert all the results to matches and add them to a map, so we can keep
817   // the most relevant match for each result.
818   base::TimeTicks start_time(base::TimeTicks::Now());
819   MatchMap map;
820   const base::Time no_time;
821   int did_not_accept_keyword_suggestion =
822       keyword_results_.suggest_results.empty() ?
823       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE :
824       TemplateURLRef::NO_SUGGESTION_CHOSEN;
825
826   bool relevance_from_server;
827   int verbatim_relevance = GetVerbatimRelevance(&relevance_from_server);
828   int did_not_accept_default_suggestion =
829       default_results_.suggest_results.empty() ?
830       TemplateURLRef::NO_SUGGESTIONS_AVAILABLE :
831       TemplateURLRef::NO_SUGGESTION_CHOSEN;
832   const TemplateURL* keyword_url = providers_.GetKeywordProviderURL();
833   if (verbatim_relevance > 0) {
834     const base::string16& trimmed_verbatim =
835         base::CollapseWhitespace(input_.text(), false);
836
837     // Verbatim results don't get suggestions and hence, answers.
838     // Scan previous matches if the last answer-bearing suggestion matches
839     // verbatim, and if so, copy over answer contents.
840     base::string16 answer_contents;
841     base::string16 answer_type;
842     scoped_ptr<SuggestionAnswer> answer;
843     for (ACMatches::iterator it = matches_.begin(); it != matches_.end();
844          ++it) {
845       if (it->answer && it->fill_into_edit == trimmed_verbatim) {
846         answer_contents = it->answer_contents;
847         answer_type = it->answer_type;
848         answer = SuggestionAnswer::copy(it->answer.get());
849         break;
850       }
851     }
852
853     SearchSuggestionParser::SuggestResult verbatim(
854         trimmed_verbatim, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
855         trimmed_verbatim, base::string16(), base::string16(), answer_contents,
856         answer_type, answer.Pass(), std::string(), std::string(), false,
857         verbatim_relevance, relevance_from_server, false, trimmed_verbatim);
858     AddMatchToMap(verbatim, std::string(), did_not_accept_default_suggestion,
859                   false, keyword_url != NULL, &map);
860   }
861   if (!keyword_input_.text().empty()) {
862     // We only create the verbatim search query match for a keyword
863     // if it's not an extension keyword.  Extension keywords are handled
864     // in KeywordProvider::Start().  (Extensions are complicated...)
865     // Note: in this provider, SEARCH_OTHER_ENGINE must correspond
866     // to the keyword verbatim search query.  Do not create other matches
867     // of type SEARCH_OTHER_ENGINE.
868     if (keyword_url &&
869         (keyword_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION)) {
870       bool keyword_relevance_from_server;
871       const int keyword_verbatim_relevance =
872           GetKeywordVerbatimRelevance(&keyword_relevance_from_server);
873       if (keyword_verbatim_relevance > 0) {
874         const base::string16& trimmed_verbatim =
875             base::CollapseWhitespace(keyword_input_.text(), false);
876         SearchSuggestionParser::SuggestResult verbatim(
877             trimmed_verbatim, AutocompleteMatchType::SEARCH_OTHER_ENGINE,
878             trimmed_verbatim, base::string16(), base::string16(),
879             base::string16(), base::string16(), nullptr, std::string(),
880             std::string(), true, keyword_verbatim_relevance,
881             keyword_relevance_from_server, false, trimmed_verbatim);
882         AddMatchToMap(verbatim, std::string(),
883                       did_not_accept_keyword_suggestion, false, true, &map);
884       }
885     }
886   }
887   AddRawHistoryResultsToMap(true, did_not_accept_keyword_suggestion, &map);
888   AddRawHistoryResultsToMap(false, did_not_accept_default_suggestion, &map);
889
890   AddSuggestResultsToMap(keyword_results_.suggest_results,
891                          keyword_results_.metadata, &map);
892   AddSuggestResultsToMap(default_results_.suggest_results,
893                          default_results_.metadata, &map);
894
895   ACMatches matches;
896   for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i)
897     matches.push_back(i->second);
898
899   AddNavigationResultsToMatches(keyword_results_.navigation_results, &matches);
900   AddNavigationResultsToMatches(default_results_.navigation_results, &matches);
901
902   // Now add the most relevant matches to |matches_|.  We take up to kMaxMatches
903   // suggest/navsuggest matches, regardless of origin.  We always include in
904   // that set a legal default match if possible.  If Instant Extended is enabled
905   // and we have server-provided (and thus hopefully more accurate) scores for
906   // some suggestions, we allow more of those, until we reach
907   // AutocompleteResult::kMaxMatches total matches (that is, enough to fill the
908   // whole popup).
909   //
910   // We will always return any verbatim matches, no matter how we obtained their
911   // scores, unless we have already accepted AutocompleteResult::kMaxMatches
912   // higher-scoring matches under the conditions above.
913   std::sort(matches.begin(), matches.end(), &AutocompleteMatch::MoreRelevant);
914
915   // Guarantee that if there's a legal default match anywhere in the result
916   // set that it'll get returned.  The rotate() call does this by moving the
917   // default match to the front of the list.
918   ACMatches::iterator default_match = FindTopMatch(&matches);
919   if (default_match != matches.end())
920     std::rotate(matches.begin(), default_match, default_match + 1);
921
922   // It's possible to get a copy of an answer from previous matches and get the
923   // same or a different answer to another server-provided suggestion.  In the
924   // future we may decide that we want to have answers attached to multiple
925   // suggestions, but the current assumption is that there should only ever be
926   // one suggestion with an answer.  To maintain this assumption, remove any
927   // answers after the first.
928   RemoveExtraAnswers(&matches);
929
930   matches_.clear();
931   size_t num_suggestions = 0;
932   for (ACMatches::const_iterator i(matches.begin());
933        (i != matches.end()) &&
934            (matches_.size() < AutocompleteResult::kMaxMatches);
935        ++i) {
936     // SEARCH_OTHER_ENGINE is only used in the SearchProvider for the keyword
937     // verbatim result, so this condition basically means "if this match is a
938     // suggestion of some sort".
939     if ((i->type != AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED) &&
940         (i->type != AutocompleteMatchType::SEARCH_OTHER_ENGINE)) {
941       // If we've already hit the limit on non-server-scored suggestions, and
942       // this isn't a server-scored suggestion we can add, skip it.
943       if ((num_suggestions >= kMaxMatches) &&
944           (!chrome::IsInstantExtendedAPIEnabled() ||
945            (i->GetAdditionalInfo(kRelevanceFromServerKey) != kTrue))) {
946         continue;
947       }
948
949       ++num_suggestions;
950     }
951
952     matches_.push_back(*i);
953   }
954   UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.ConvertResultsTime",
955                       base::TimeTicks::Now() - start_time);
956 }
957
958 void SearchProvider::RemoveExtraAnswers(ACMatches* matches) {
959   bool answer_seen = false;
960   for (ACMatches::iterator it = matches->begin(); it != matches->end(); ++it) {
961     if (it->answer) {
962       if (!answer_seen) {
963         answer_seen = true;
964       } else {
965         it->answer_contents.clear();
966         it->answer_type.clear();
967         it->answer.reset();
968       }
969     }
970   }
971 }
972
973 ACMatches::const_iterator SearchProvider::FindTopMatch() const {
974   ACMatches::const_iterator it = matches_.begin();
975   while ((it != matches_.end()) && !it->allowed_to_be_default_match)
976     ++it;
977   return it;
978 }
979
980 bool SearchProvider::IsTopMatchSearchWithURLInput() const {
981   ACMatches::const_iterator first_match = FindTopMatch();
982   return (input_.type() == metrics::OmniboxInputType::URL) &&
983       (first_match != matches_.end()) &&
984       (first_match->relevance > CalculateRelevanceForVerbatim()) &&
985       (first_match->type != AutocompleteMatchType::NAVSUGGEST) &&
986       (first_match->type != AutocompleteMatchType::NAVSUGGEST_PERSONALIZED);
987 }
988
989 void SearchProvider::AddNavigationResultsToMatches(
990     const SearchSuggestionParser::NavigationResults& navigation_results,
991     ACMatches* matches) {
992   for (SearchSuggestionParser::NavigationResults::const_iterator it =
993            navigation_results.begin(); it != navigation_results.end(); ++it) {
994     matches->push_back(NavigationToMatch(*it));
995     // In the absence of suggested relevance scores, use only the single
996     // highest-scoring result.  (The results are already sorted by relevance.)
997     if (!it->relevance_from_server())
998       return;
999   }
1000 }
1001
1002 void SearchProvider::AddRawHistoryResultsToMap(bool is_keyword,
1003                                                int did_not_accept_suggestion,
1004                                                MatchMap* map) {
1005   const HistoryResults& raw_results =
1006       is_keyword ? raw_keyword_history_results_ : raw_default_history_results_;
1007   if (!OmniboxFieldTrial::EnableAnswersInSuggest() && raw_results.empty())
1008     return;
1009
1010   base::TimeTicks start_time(base::TimeTicks::Now());
1011
1012   // Until Answers becomes default, scoring of history results will still happen
1013   // here for non-Answers Chrome, to prevent scoring performance regressions
1014   // resulting from moving the scoring code before the suggest request is sent.
1015   // For users with Answers enabled, the history results have already been
1016   // scored earlier, right after calling DoHistoryQuery().
1017   SearchSuggestionParser::SuggestResults local_transformed_results;
1018   const SearchSuggestionParser::SuggestResults* transformed_results = NULL;
1019   if (!OmniboxFieldTrial::EnableAnswersInSuggest()) {
1020     ScoreHistoryResults(raw_results, is_keyword, &local_transformed_results);
1021     transformed_results = &local_transformed_results;
1022   } else {
1023     transformed_results = is_keyword ? &transformed_keyword_history_results_
1024                                      : &transformed_default_history_results_;
1025   }
1026   DCHECK(transformed_results);
1027   AddTransformedHistoryResultsToMap(
1028       *transformed_results, did_not_accept_suggestion, map);
1029   UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime",
1030                       base::TimeTicks::Now() - start_time);
1031 }
1032
1033 void SearchProvider::AddTransformedHistoryResultsToMap(
1034     const SearchSuggestionParser::SuggestResults& transformed_results,
1035     int did_not_accept_suggestion,
1036     MatchMap* map) {
1037   for (SearchSuggestionParser::SuggestResults::const_iterator i(
1038            transformed_results.begin());
1039        i != transformed_results.end();
1040        ++i) {
1041     AddMatchToMap(*i, std::string(), did_not_accept_suggestion, true,
1042                   providers_.GetKeywordProviderURL() != NULL, map);
1043   }
1044 }
1045
1046 SearchSuggestionParser::SuggestResults
1047 SearchProvider::ScoreHistoryResultsHelper(const HistoryResults& results,
1048                                           bool base_prevent_inline_autocomplete,
1049                                           bool input_multiple_words,
1050                                           const base::string16& input_text,
1051                                           bool is_keyword) {
1052   SearchSuggestionParser::SuggestResults scored_results;
1053   // True if the user has asked this exact query previously.
1054   bool found_what_you_typed_match = false;
1055   const bool prevent_search_history_inlining =
1056       OmniboxFieldTrial::SearchHistoryPreventInlining(
1057           input_.current_page_classification());
1058   const base::string16& trimmed_input =
1059       base::CollapseWhitespace(input_text, false);
1060   for (HistoryResults::const_iterator i(results.begin()); i != results.end();
1061        ++i) {
1062     const base::string16& trimmed_suggestion =
1063         base::CollapseWhitespace(i->term, false);
1064
1065     // Don't autocomplete multi-word queries that have only been seen once
1066     // unless the user has typed more than one word.
1067     bool prevent_inline_autocomplete = base_prevent_inline_autocomplete ||
1068         (!input_multiple_words && (i->visits < 2) &&
1069          HasMultipleWords(trimmed_suggestion));
1070
1071     int relevance = CalculateRelevanceForHistory(
1072         i->time, is_keyword, !prevent_inline_autocomplete,
1073         prevent_search_history_inlining);
1074     // Add the match to |scored_results| by putting the what-you-typed match
1075     // on the front and appending all other matches.  We want the what-you-
1076     // typed match to always be first.
1077     SearchSuggestionParser::SuggestResults::iterator insertion_position =
1078         scored_results.end();
1079     if (trimmed_suggestion == trimmed_input) {
1080       found_what_you_typed_match = true;
1081       insertion_position = scored_results.begin();
1082     }
1083     SearchSuggestionParser::SuggestResult history_suggestion(
1084         trimmed_suggestion, AutocompleteMatchType::SEARCH_HISTORY,
1085         trimmed_suggestion, base::string16(), base::string16(),
1086         base::string16(), base::string16(), nullptr, std::string(),
1087         std::string(), is_keyword, relevance, false, false, trimmed_input);
1088     // History results are synchronous; they are received on the last keystroke.
1089     history_suggestion.set_received_after_last_keystroke(false);
1090     scored_results.insert(insertion_position, history_suggestion);
1091   }
1092
1093   // History returns results sorted for us.  However, we may have docked some
1094   // results' scores, so things are no longer in order.  While keeping the
1095   // what-you-typed match at the front (if it exists), do a stable sort to get
1096   // things back in order without otherwise disturbing results with equal
1097   // scores, then force the scores to be unique, so that the order in which
1098   // they're shown is deterministic.
1099   std::stable_sort(scored_results.begin() +
1100                        (found_what_you_typed_match ? 1 : 0),
1101                    scored_results.end(),
1102                    CompareScoredResults());
1103
1104   // Don't autocomplete to search terms that would normally be treated as URLs
1105   // when typed. For example, if the user searched for "google.com" and types
1106   // "goog", don't autocomplete to the search term "google.com". Otherwise,
1107   // the input will look like a URL but act like a search, which is confusing.
1108   // The 1200 relevance score threshold in the test below is the lowest
1109   // possible score in CalculateRelevanceForHistory()'s aggressive-scoring
1110   // curve.  This is an appropriate threshold to use to decide if we're overly
1111   // aggressively inlining because, if we decide the answer is yes, the
1112   // way we resolve it it to not use the aggressive-scoring curve.
1113   // NOTE: We don't check for autocompleting to URLs in the following cases:
1114   //  * When inline autocomplete is disabled, we won't be inline autocompleting
1115   //    this term, so we don't need to worry about confusion as much.  This
1116   //    also prevents calling Classify() again from inside the classifier
1117   //    (which will corrupt state and likely crash), since the classifier
1118   //    always disables inline autocomplete.
1119   //  * When the user has typed the whole string before as a query, then it's
1120   //    likely the user has no expectation that term should be interpreted as
1121   //    as a URL, so we need not do anything special to preserve user
1122   //    expectation.
1123   int last_relevance = 0;
1124   if (!base_prevent_inline_autocomplete && !found_what_you_typed_match &&
1125       scored_results.front().relevance() >= 1200) {
1126     AutocompleteMatch match;
1127     client_->Classify(scored_results.front().suggestion(), false, false,
1128                       input_.current_page_classification(), &match, NULL);
1129     // Demote this match that would normally be interpreted as a URL to have
1130     // the highest score a previously-issued search query could have when
1131     // scoring with the non-aggressive method.  A consequence of demoting
1132     // by revising |last_relevance| is that this match and all following
1133     // matches get demoted; the relative order of matches is preserved.
1134     // One could imagine demoting only those matches that might cause
1135     // confusion (which, by the way, might change the relative order of
1136     // matches.  We have decided to go with the simple demote-all approach
1137     // because selective demotion requires multiple Classify() calls and
1138     // such calls can be expensive (as expensive as running the whole
1139     // autocomplete system).
1140     if (!AutocompleteMatch::IsSearchType(match.type)) {
1141       last_relevance = CalculateRelevanceForHistory(
1142           base::Time::Now(), is_keyword, false,
1143           prevent_search_history_inlining);
1144     }
1145   }
1146
1147   for (SearchSuggestionParser::SuggestResults::iterator i(
1148            scored_results.begin()); i != scored_results.end(); ++i) {
1149     if ((last_relevance != 0) && (i->relevance() >= last_relevance))
1150       i->set_relevance(last_relevance - 1);
1151     last_relevance = i->relevance();
1152   }
1153
1154   return scored_results;
1155 }
1156
1157 void SearchProvider::ScoreHistoryResults(
1158     const HistoryResults& results,
1159     bool is_keyword,
1160     SearchSuggestionParser::SuggestResults* scored_results) {
1161   DCHECK(scored_results);
1162   if (results.empty()) {
1163     scored_results->clear();
1164     return;
1165   }
1166
1167   bool prevent_inline_autocomplete = input_.prevent_inline_autocomplete() ||
1168       (input_.type() == metrics::OmniboxInputType::URL);
1169   const base::string16 input_text = GetInput(is_keyword).text();
1170   bool input_multiple_words = HasMultipleWords(input_text);
1171
1172   if (!prevent_inline_autocomplete && input_multiple_words) {
1173     // ScoreHistoryResultsHelper() allows autocompletion of multi-word, 1-visit
1174     // queries if the input also has multiple words.  But if we were already
1175     // scoring a multi-word, multi-visit query aggressively, and the current
1176     // input is still a prefix of it, then changing the suggestion suddenly
1177     // feels wrong.  To detect this case, first score as if only one word has
1178     // been typed, then check if the best result came from aggressive search
1179     // history scoring.  If it did, then just keep that score set.  This
1180     // 1200 the lowest possible score in CalculateRelevanceForHistory()'s
1181     // aggressive-scoring curve.
1182     *scored_results = ScoreHistoryResultsHelper(
1183         results, prevent_inline_autocomplete, false, input_text, is_keyword);
1184     if ((scored_results->front().relevance() < 1200) ||
1185         !HasMultipleWords(scored_results->front().suggestion()))
1186       scored_results->clear();  // Didn't detect the case above, score normally.
1187   }
1188   if (scored_results->empty()) {
1189     *scored_results = ScoreHistoryResultsHelper(results,
1190                                                 prevent_inline_autocomplete,
1191                                                 input_multiple_words,
1192                                                 input_text,
1193                                                 is_keyword);
1194   }
1195 }
1196
1197 void SearchProvider::AddSuggestResultsToMap(
1198     const SearchSuggestionParser::SuggestResults& results,
1199     const std::string& metadata,
1200     MatchMap* map) {
1201   for (size_t i = 0; i < results.size(); ++i) {
1202     AddMatchToMap(results[i], metadata, i, false,
1203                   providers_.GetKeywordProviderURL() != NULL, map);
1204   }
1205 }
1206
1207 int SearchProvider::GetVerbatimRelevance(bool* relevance_from_server) const {
1208   // Use the suggested verbatim relevance score if it is non-negative (valid),
1209   // if inline autocomplete isn't prevented (always show verbatim on backspace),
1210   // and if it won't suppress verbatim, leaving no default provider matches.
1211   // Otherwise, if the default provider returned no matches and was still able
1212   // to suppress verbatim, the user would have no search/nav matches and may be
1213   // left unable to search using their default provider from the omnibox.
1214   // Check for results on each verbatim calculation, as results from older
1215   // queries (on previous input) may be trimmed for failing to inline new input.
1216   bool use_server_relevance =
1217       (default_results_.verbatim_relevance >= 0) &&
1218       !input_.prevent_inline_autocomplete() &&
1219       ((default_results_.verbatim_relevance > 0) ||
1220        !default_results_.suggest_results.empty() ||
1221        !default_results_.navigation_results.empty());
1222   if (relevance_from_server)
1223     *relevance_from_server = use_server_relevance;
1224   return use_server_relevance ?
1225       default_results_.verbatim_relevance : CalculateRelevanceForVerbatim();
1226 }
1227
1228 int SearchProvider::CalculateRelevanceForVerbatim() const {
1229   if (!providers_.keyword_provider().empty())
1230     return 250;
1231   return CalculateRelevanceForVerbatimIgnoringKeywordModeState();
1232 }
1233
1234 int SearchProvider::
1235     CalculateRelevanceForVerbatimIgnoringKeywordModeState() const {
1236   switch (input_.type()) {
1237     case metrics::OmniboxInputType::UNKNOWN:
1238     case metrics::OmniboxInputType::QUERY:
1239     case metrics::OmniboxInputType::FORCED_QUERY:
1240       return kNonURLVerbatimRelevance;
1241
1242     case metrics::OmniboxInputType::URL:
1243       return 850;
1244
1245     default:
1246       NOTREACHED();
1247       return 0;
1248   }
1249 }
1250
1251 int SearchProvider::GetKeywordVerbatimRelevance(
1252     bool* relevance_from_server) const {
1253   // Use the suggested verbatim relevance score if it is non-negative (valid),
1254   // if inline autocomplete isn't prevented (always show verbatim on backspace),
1255   // and if it won't suppress verbatim, leaving no keyword provider matches.
1256   // Otherwise, if the keyword provider returned no matches and was still able
1257   // to suppress verbatim, the user would have no search/nav matches and may be
1258   // left unable to search using their keyword provider from the omnibox.
1259   // Check for results on each verbatim calculation, as results from older
1260   // queries (on previous input) may be trimmed for failing to inline new input.
1261   bool use_server_relevance =
1262       (keyword_results_.verbatim_relevance >= 0) &&
1263       !input_.prevent_inline_autocomplete() &&
1264       ((keyword_results_.verbatim_relevance > 0) ||
1265        !keyword_results_.suggest_results.empty() ||
1266        !keyword_results_.navigation_results.empty());
1267   if (relevance_from_server)
1268     *relevance_from_server = use_server_relevance;
1269   return use_server_relevance ?
1270       keyword_results_.verbatim_relevance :
1271       CalculateRelevanceForKeywordVerbatim(keyword_input_.type(),
1272                                            keyword_input_.prefer_keyword());
1273 }
1274
1275 int SearchProvider::CalculateRelevanceForHistory(
1276     const base::Time& time,
1277     bool is_keyword,
1278     bool use_aggressive_method,
1279     bool prevent_search_history_inlining) const {
1280   // The relevance of past searches falls off over time. There are two distinct
1281   // equations used. If the first equation is used (searches to the primary
1282   // provider that we want to score aggressively), the score is in the range
1283   // 1300-1599 (unless |prevent_search_history_inlining|, in which case
1284   // it's in the range 1200-1299). If the second equation is used the
1285   // relevance of a search 15 minutes ago is discounted 50 points, while the
1286   // relevance of a search two weeks ago is discounted 450 points.
1287   double elapsed_time = std::max((base::Time::Now() - time).InSecondsF(), 0.0);
1288   bool is_primary_provider = is_keyword || !providers_.has_keyword_provider();
1289   if (is_primary_provider && use_aggressive_method) {
1290     // Searches with the past two days get a different curve.
1291     const double autocomplete_time = 2 * 24 * 60 * 60;
1292     if (elapsed_time < autocomplete_time) {
1293       int max_score = is_keyword ? 1599 : 1399;
1294       if (prevent_search_history_inlining)
1295         max_score = 1299;
1296       return max_score - static_cast<int>(99 *
1297           std::pow(elapsed_time / autocomplete_time, 2.5));
1298     }
1299     elapsed_time -= autocomplete_time;
1300   }
1301
1302   const int score_discount =
1303       static_cast<int>(6.5 * std::pow(elapsed_time, 0.3));
1304
1305   // Don't let scores go below 0.  Negative relevance scores are meaningful in
1306   // a different way.
1307   int base_score;
1308   if (is_primary_provider)
1309     base_score = (input_.type() == metrics::OmniboxInputType::URL) ? 750 : 1050;
1310   else
1311     base_score = 200;
1312   return std::max(0, base_score - score_discount);
1313 }
1314
1315 AutocompleteMatch SearchProvider::NavigationToMatch(
1316     const SearchSuggestionParser::NavigationResult& navigation) {
1317   base::string16 input;
1318   const bool trimmed_whitespace = base::TrimWhitespace(
1319       navigation.from_keyword_provider() ?
1320           keyword_input_.text() : input_.text(),
1321       base::TRIM_TRAILING, &input) != base::TRIM_NONE;
1322   AutocompleteMatch match(this, navigation.relevance(), false,
1323                           navigation.type());
1324   match.destination_url = navigation.url();
1325   BaseSearchProvider::SetDeletionURL(navigation.deletion_url(), &match);
1326   // First look for the user's input inside the formatted url as it would be
1327   // without trimming the scheme, so we can find matches at the beginning of the
1328   // scheme.
1329   const URLPrefix* prefix =
1330       URLPrefix::BestURLPrefix(navigation.formatted_url(), input);
1331   size_t match_start = (prefix == NULL) ?
1332       navigation.formatted_url().find(input) : prefix->prefix.length();
1333   bool trim_http = !AutocompleteInput::HasHTTPScheme(input) &&
1334       (!prefix || (match_start != 0));
1335   const net::FormatUrlTypes format_types =
1336       net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP);
1337
1338   const std::string languages(client_->AcceptLanguages());
1339   size_t inline_autocomplete_offset = (prefix == NULL) ?
1340       base::string16::npos : (match_start + input.length());
1341   match.fill_into_edit +=
1342       AutocompleteInput::FormattedStringWithEquivalentMeaning(
1343           navigation.url(),
1344           net::FormatUrl(navigation.url(), languages, format_types,
1345                          net::UnescapeRule::SPACES, NULL, NULL,
1346                          &inline_autocomplete_offset),
1347           client_->SchemeClassifier());
1348   // Preserve the forced query '?' prefix in |match.fill_into_edit|.
1349   // Otherwise, user edits to a suggestion would show non-Search results.
1350   if (input_.type() == metrics::OmniboxInputType::FORCED_QUERY) {
1351     match.fill_into_edit.insert(0, base::ASCIIToUTF16("?"));
1352     if (inline_autocomplete_offset != base::string16::npos)
1353       ++inline_autocomplete_offset;
1354   }
1355   if (inline_autocomplete_offset != base::string16::npos) {
1356     DCHECK(inline_autocomplete_offset <= match.fill_into_edit.length());
1357     match.inline_autocompletion =
1358         match.fill_into_edit.substr(inline_autocomplete_offset);
1359   }
1360   // An inlineable navsuggestion can only be the default match when there
1361   // is no keyword provider active, lest it appear first and break the user
1362   // out of keyword mode.  We also must have received the navsuggestion before
1363   // the last keystroke, to prevent asynchronous inline autocompletions changes.
1364   // The navsuggestion can also only be default if either the inline
1365   // autocompletion is empty or we're not preventing inline autocompletion.
1366   // Finally, if we have an inlineable navsuggestion with an inline completion
1367   // that we're not preventing, make sure we didn't trim any whitespace.
1368   // We don't want to claim http://foo.com/bar is inlineable against the
1369   // input "foo.com/b ".
1370   match.allowed_to_be_default_match =
1371       (prefix != NULL) &&
1372       (providers_.GetKeywordProviderURL() == NULL) &&
1373       !navigation.received_after_last_keystroke() &&
1374       (match.inline_autocompletion.empty() ||
1375       (!input_.prevent_inline_autocomplete() && !trimmed_whitespace));
1376   match.EnsureUWYTIsAllowedToBeDefault(
1377       input_.canonicalized_url(), providers_.template_url_service());
1378
1379   match.contents = navigation.match_contents();
1380   match.contents_class = navigation.match_contents_class();
1381   match.description = navigation.description();
1382   AutocompleteMatch::ClassifyMatchInString(input, match.description,
1383       ACMatchClassification::NONE, &match.description_class);
1384
1385   match.RecordAdditionalInfo(
1386       kRelevanceFromServerKey,
1387       navigation.relevance_from_server() ? kTrue : kFalse);
1388   match.RecordAdditionalInfo(kShouldPrefetchKey, kFalse);
1389
1390   return match;
1391 }
1392
1393 void SearchProvider::UpdateDone() {
1394   // We're done when the timer isn't running, there are no suggest queries
1395   // pending, and we're not waiting on Instant.
1396   done_ = !timer_.IsRunning() && (suggest_results_pending_ == 0);
1397 }
1398
1399 std::string SearchProvider::GetSessionToken() {
1400   base::TimeTicks current_time(base::TimeTicks::Now());
1401   // Renew token if it expired.
1402   if (current_time > token_expiration_time_) {
1403     const size_t kTokenBytes = 12;
1404     std::string raw_data;
1405     base::RandBytes(WriteInto(&raw_data, kTokenBytes + 1), kTokenBytes);
1406     base::Base64Encode(raw_data, &current_token_);
1407
1408     // Make the base64 encoded value URL and filename safe(see RFC 3548).
1409     std::replace(current_token_.begin(), current_token_.end(), '+', '-');
1410     std::replace(current_token_.begin(), current_token_.end(), '/', '_');
1411   }
1412
1413   // Extend expiration time another 60 seconds.
1414   token_expiration_time_ = current_time + base::TimeDelta::FromSeconds(60);
1415
1416   return current_token_;
1417 }
1418
1419 void SearchProvider::RegisterDisplayedAnswers(
1420     const AutocompleteResult& result) {
1421   if (result.empty())
1422     return;
1423
1424   // The answer must be in the first or second slot to be considered. It should
1425   // only be in the second slot if AutocompleteController ranked a local search
1426   // history or a verbatim item higher than the answer.
1427   AutocompleteResult::const_iterator match = result.begin();
1428   if (match->answer_contents.empty() && result.size() > 1)
1429     ++match;
1430   if (match->answer_contents.empty() || match->answer_type.empty() ||
1431       match->fill_into_edit.empty())
1432     return;
1433
1434   // Valid answer encountered, cache it for further queries.
1435   answers_cache_.UpdateRecentAnswers(match->fill_into_edit, match->answer_type);
1436 }
1437
1438 AnswersQueryData SearchProvider::FindAnswersPrefetchData() {
1439   // Retrieve the top entry from scored history results.
1440   MatchMap map;
1441   AddTransformedHistoryResultsToMap(transformed_keyword_history_results_,
1442                                     TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
1443                                     &map);
1444   AddTransformedHistoryResultsToMap(transformed_default_history_results_,
1445                                     TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
1446                                     &map);
1447
1448   ACMatches matches;
1449   for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i)
1450     matches.push_back(i->second);
1451   std::sort(matches.begin(), matches.end(), &AutocompleteMatch::MoreRelevant);
1452
1453   // If there is a top scoring entry, find the corresponding answer.
1454   if (!matches.empty())
1455     return answers_cache_.GetTopAnswerEntry(matches[0].contents);
1456
1457   return AnswersQueryData();
1458 }