1// Copyright (c) 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// This file contains the Search autocomplete provider.  This provider is
6// responsible for all autocomplete entries that start with "Search <engine>
7// for ...", including searching for the current input string, search
8// history, and search suggestions.  An instance of it gets created and
9// managed by the autocomplete controller.
10
11#ifndef CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
12#define CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
13
14#include <map>
15#include <string>
16#include <vector>
17
18#include "base/basictypes.h"
19#include "base/compiler_specific.h"
20#include "base/memory/scoped_ptr.h"
21#include "base/time/time.h"
22#include "base/timer/timer.h"
23#include "chrome/browser/autocomplete/autocomplete_input.h"
24#include "chrome/browser/autocomplete/autocomplete_match.h"
25#include "chrome/browser/autocomplete/autocomplete_provider.h"
26#include "chrome/browser/history/history_types.h"
27#include "chrome/browser/search_engines/template_url.h"
28#include "net/url_request/url_fetcher_delegate.h"
29
30class Profile;
31class SearchProviderTest;
32class TemplateURLService;
33
34namespace base {
35class Value;
36}
37
38namespace net {
39class URLFetcher;
40}
41
42// Autocomplete provider for searches and suggestions from a search engine.
43//
44// After construction, the autocomplete controller repeatedly calls Start()
45// with some user input, each time expecting to receive a small set of the best
46// matches (either synchronously or asynchronously).
47//
48// Initially the provider creates a match that searches for the current input
49// text.  It also starts a task to query the Suggest servers.  When that data
50// comes back, the provider creates and returns matches for the best
51// suggestions.
52class SearchProvider : public AutocompleteProvider,
53                       public net::URLFetcherDelegate {
54 public:
55  // ID used in creating URLFetcher for default provider's suggest results.
56  static const int kDefaultProviderURLFetcherID;
57
58  // ID used in creating URLFetcher for keyword provider's suggest results.
59  static const int kKeywordProviderURLFetcherID;
60
61  SearchProvider(AutocompleteProviderListener* listener, Profile* profile);
62
63  // Returns an AutocompleteMatch with the given |autocomplete_provider|,
64  // |relevance|, and |type|, which represents a search via |template_url| for
65  // |query_string|.  If |template_url| is NULL, returns a match with an invalid
66  // destination URL.
67  //
68  // |input_text| is the original user input, which may differ from
69  // |query_string|; e.g. the user typed "foo" and got a search suggestion of
70  // "food", which we're now marking up.  This is used to highlight portions of
71  // the match contents to distinguish locally-typed text from suggested text.
72  //
73  // |input| and |is_keyword| are necessary for various other details, like
74  // whether we should allow inline autocompletion and what the transition type
75  // should be.  |accepted_suggestion| and |omnibox_start_margin| are used along
76  // with |input_text| to generate Assisted Query Stats.
77  // |append_extra_query_params| should be set if |template_url| is the default
78  // search engine, so the destination URL will contain any
79  // command-line-specified query params.
80  static AutocompleteMatch CreateSearchSuggestion(
81      AutocompleteProvider* autocomplete_provider,
82      int relevance,
83      AutocompleteMatch::Type type,
84      const TemplateURL* template_url,
85      const string16& query_string,
86      const string16& input_text,
87      const AutocompleteInput& input,
88      bool is_keyword,
89      int accepted_suggestion,
90      int omnibox_start_margin,
91      bool append_extra_query_params);
92
93  // AutocompleteProvider:
94  virtual void AddProviderInfo(ProvidersInfo* provider_info) const OVERRIDE;
95  virtual void ResetSession() OVERRIDE;
96
97  bool field_trial_triggered_in_session() const {
98    return field_trial_triggered_in_session_;
99  }
100
101 private:
102  // TODO(hfung): Remove ZeroSuggestProvider as a friend class after
103  // refactoring common code to a new base class.
104  friend class SearchProviderTest;
105  friend class ZeroSuggestProvider;
106  FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, NavigationInline);
107  FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, NavigationInlineDomainClassify);
108  FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, NavigationInlineSchemeSubstring);
109  FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, RemoveStaleResultsTest);
110  FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, SuggestRelevanceExperiment);
111  FRIEND_TEST_ALL_PREFIXES(AutocompleteProviderTest, GetDestinationURL);
112
113  // Manages the providers (TemplateURLs) used by SearchProvider. Two providers
114  // may be used:
115  // . The default provider. This corresponds to the user's default search
116  //   engine. This is always used, except for the rare case of no default
117  //   engine.
118  // . The keyword provider. This is used if the user has typed in a keyword.
119  class Providers {
120   public:
121    explicit Providers(TemplateURLService* template_url_service);
122
123    // Returns true if the specified providers match the two providers cached
124    // by this class.
125    bool equal(const string16& default_provider,
126               const string16& keyword_provider) const {
127      return (default_provider == default_provider_) &&
128          (keyword_provider == keyword_provider_);
129    }
130
131    // Resets the cached providers.
132    void set(const string16& default_provider,
133             const string16& keyword_provider) {
134      default_provider_ = default_provider;
135      keyword_provider_ = keyword_provider;
136    }
137
138    TemplateURLService* template_url_service() { return template_url_service_; }
139    const string16& default_provider() const { return default_provider_; }
140    const string16& keyword_provider() const { return keyword_provider_; }
141
142    // NOTE: These may return NULL even if the provider members are nonempty!
143    const TemplateURL* GetDefaultProviderURL() const;
144    const TemplateURL* GetKeywordProviderURL() const;
145
146    // Returns true if there is a valid keyword provider.
147    bool has_keyword_provider() const { return !keyword_provider_.empty(); }
148
149   private:
150    TemplateURLService* template_url_service_;
151
152    // Cached across the life of a query so we behave consistently even if the
153    // user changes their default while the query is running.
154    string16 default_provider_;
155    string16 keyword_provider_;
156
157    DISALLOW_COPY_AND_ASSIGN(Providers);
158  };
159
160  // The Result classes are intermediate representations of AutocompleteMatches,
161  // simply containing relevance-ranked search and navigation suggestions.
162  // They may be cached to provide some synchronous matches while requests for
163  // new suggestions from updated input are in flight.
164  // TODO(msw) Extend these classes to generate their corresponding matches and
165  //           other requisite data, in order to consolidate and simplify the
166  //           highly fragmented SearchProvider logic for each Result type.
167  class Result {
168   public:
169    Result(bool from_keyword_provider,
170           int relevance,
171           bool relevance_from_server);
172    virtual ~Result();
173
174    bool from_keyword_provider() const { return from_keyword_provider_; }
175
176    int relevance() const { return relevance_; }
177    void set_relevance(int relevance) { relevance_ = relevance; }
178
179    bool relevance_from_server() const { return relevance_from_server_; }
180    void set_relevance_from_server(bool relevance_from_server) {
181      relevance_from_server_ = relevance_from_server;
182    }
183
184    // Returns if this result is inlineable against the current input |input|.
185    // Non-inlineable results are stale.
186    virtual bool IsInlineable(const string16& input) const = 0;
187
188    // Returns the default relevance value for this result (which may
189    // be left over from a previous omnibox input) given the current
190    // input and whether the current input caused a keyword provider
191    // to be active.
192    virtual int CalculateRelevance(const AutocompleteInput& input,
193                                   bool keyword_provider_requested) const = 0;
194
195   protected:
196    // True if the result came from the keyword provider.
197    bool from_keyword_provider_;
198
199    // The relevance score.
200    int relevance_;
201
202   private:
203    // Whether this result's relevance score was fully or partly calculated
204    // based on server information, and thus is assumed to be more accurate.
205    // This is ultimately used in
206    // SearchProvider::ConvertResultsToAutocompleteMatches(), see comments
207    // there.
208    bool relevance_from_server_;
209  };
210
211  class SuggestResult : public Result {
212   public:
213    SuggestResult(const string16& suggestion,
214                  bool from_keyword_provider,
215                  int relevance,
216                  bool relevance_from_server);
217    virtual ~SuggestResult();
218
219    const string16& suggestion() const { return suggestion_; }
220
221    // Result:
222    virtual bool IsInlineable(const string16& input) const OVERRIDE;
223    virtual int CalculateRelevance(
224        const AutocompleteInput& input,
225        bool keyword_provider_requested) const OVERRIDE;
226
227   private:
228    // The search suggestion string.
229    string16 suggestion_;
230  };
231
232  class NavigationResult : public Result {
233   public:
234    // |provider| is necessary to use StringForURLDisplay() in order to
235    // compute |formatted_url_|.
236    NavigationResult(const AutocompleteProvider& provider,
237                     const GURL& url,
238                     const string16& description,
239                     bool from_keyword_provider,
240                     int relevance,
241                     bool relevance_from_server);
242    virtual ~NavigationResult();
243
244    const GURL& url() const { return url_; }
245    const string16& description() const { return description_; }
246    const string16& formatted_url() const { return formatted_url_; }
247
248    // Result:
249    virtual bool IsInlineable(const string16& input) const OVERRIDE;
250    virtual int CalculateRelevance(
251        const AutocompleteInput& input,
252        bool keyword_provider_requested) const OVERRIDE;
253
254   private:
255    // The suggested url for navigation.
256    GURL url_;
257
258    // The properly formatted ("fixed up") URL string with equivalent meaning
259    // to the one in |url_|.
260    string16 formatted_url_;
261
262    // The suggested navigational result description; generally the site name.
263    string16 description_;
264  };
265
266  class CompareScoredResults;
267
268  typedef std::vector<SuggestResult> SuggestResults;
269  typedef std::vector<NavigationResult> NavigationResults;
270  typedef std::vector<history::KeywordSearchTermVisit> HistoryResults;
271  typedef std::map<string16, AutocompleteMatch> MatchMap;
272
273  // A simple structure bundling most of the information (including
274  // both SuggestResults and NavigationResults) returned by a call to
275  // the suggest server.
276  //
277  // This has to be declared after the typedefs since it relies on some of them.
278  struct Results {
279    Results();
280    ~Results();
281
282    // Clears |suggest_results| and |navigation_results| and resets
283    // |verbatim_relevance| to -1 (implies unset).
284    void Clear();
285
286    // Returns whether any of the results (including verbatim) have
287    // server-provided scores.
288    bool HasServerProvidedScores() const;
289
290    // Query suggestions sorted by relevance score.
291    SuggestResults suggest_results;
292
293    // Navigational suggestions sorted by relevance score.
294    NavigationResults navigation_results;
295
296    // The server supplied verbatim relevance scores. Negative values
297    // indicate that there is no suggested score; a value of 0
298    // suppresses the verbatim result.
299    int verbatim_relevance;
300
301   private:
302    DISALLOW_COPY_AND_ASSIGN(Results);
303  };
304
305  virtual ~SearchProvider();
306
307  // Removes non-inlineable results until either the top result can inline
308  // autocomplete the current input or verbatim outscores the top result.
309  static void RemoveStaleResults(const string16& input,
310                                 int verbatim_relevance,
311                                 SuggestResults* suggest_results,
312                                 NavigationResults* navigation_results);
313
314  // Calculates the relevance score for the keyword verbatim result (if the
315  // input matches one of the profile's keyword).
316  static int CalculateRelevanceForKeywordVerbatim(AutocompleteInput::Type type,
317                                                  bool prefer_keyword);
318
319  // AutocompleteProvider:
320  virtual void Start(const AutocompleteInput& input,
321                     bool minimal_changes) OVERRIDE;
322  virtual void Stop(bool clear_cached_results) OVERRIDE;
323
324  // net::URLFetcherDelegate:
325  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
326
327  // Called when timer_ expires.
328  void Run();
329
330  // Runs the history query, if necessary. The history query is synchronous.
331  // This does not update |done_|.
332  void DoHistoryQuery(bool minimal_changes);
333
334  // Determines whether an asynchronous subcomponent query should run for the
335  // current input.  If so, starts it if necessary; otherwise stops it.
336  // NOTE: This function does not update |done_|.  Callers must do so.
337  void StartOrStopSuggestQuery(bool minimal_changes);
338
339  // Returns true when the current query can be sent to the Suggest service.
340  // This will be false e.g. when Suggest is disabled, the query contains
341  // potentially private data, etc.
342  bool IsQuerySuitableForSuggest() const;
343
344  // Stops the suggest query.
345  // NOTE: This does not update |done_|.  Callers must do so.
346  void StopSuggest();
347
348  // Clears the current results.
349  void ClearAllResults();
350
351  // Removes stale results for both default and keyword providers.  See comments
352  // on RemoveStaleResults().
353  void RemoveAllStaleResults();
354
355  // Apply calculated relevance scores to the current results.
356  void ApplyCalculatedRelevance();
357  void ApplyCalculatedSuggestRelevance(SuggestResults* list);
358  void ApplyCalculatedNavigationRelevance(NavigationResults* list);
359
360  // Starts a new URLFetcher requesting suggest results from |template_url|;
361  // callers own the returned URLFetcher, which is NULL for invalid providers.
362  net::URLFetcher* CreateSuggestFetcher(int id,
363                                        const TemplateURL* template_url,
364                                        const AutocompleteInput& input);
365
366  // Parses results from the suggest server and updates the appropriate suggest
367  // and navigation result lists, depending on whether |is_keyword| is true.
368  // Returns whether the appropriate result list members were updated.
369  bool ParseSuggestResults(base::Value* root_val, bool is_keyword);
370
371  // Converts the parsed results to a set of AutocompleteMatches, |matches_|.
372  void ConvertResultsToAutocompleteMatches();
373
374  // Checks if suggested relevances violate certain expected constraints.
375  // See UpdateMatches() for the use and explanation of these constraints.
376  bool IsTopMatchNavigationInKeywordMode() const;
377  bool IsTopMatchScoreTooLow() const;
378  bool IsTopMatchSearchWithURLInput() const;
379  bool HasValidDefaultMatch(
380      bool autocomplete_result_will_reorder_for_default_match) const;
381
382  // Updates |matches_| from the latest results; applies calculated relevances
383  // if suggested relevances cause undesriable behavior. Updates |done_|.
384  void UpdateMatches();
385
386  // Converts an appropriate number of navigation results in
387  // |navigation_results| to matches and adds them to |matches|.
388  void AddNavigationResultsToMatches(
389      const NavigationResults& navigation_results,
390      ACMatches* matches);
391
392  // Adds a match for each result in |results| to |map|. |is_keyword| indicates
393  // whether the results correspond to the keyword provider or default provider.
394  void AddHistoryResultsToMap(const HistoryResults& results,
395                              bool is_keyword,
396                              int did_not_accept_suggestion,
397                              MatchMap* map);
398
399  // Calculates relevance scores for all |results|.
400  SuggestResults ScoreHistoryResults(const HistoryResults& results,
401                                     bool base_prevent_inline_autocomplete,
402                                     bool input_multiple_words,
403                                     const string16& input_text,
404                                     bool is_keyword);
405
406  // Adds matches for |results| to |map|.
407  void AddSuggestResultsToMap(const SuggestResults& results, MatchMap* map);
408
409  // Gets the relevance score for the verbatim result.  This value may be
410  // provided by the suggest server or calculated locally; if
411  // |relevance_from_server| is non-NULL, it will be set to indicate which of
412  // those is true.
413  int GetVerbatimRelevance(bool* relevance_from_server) const;
414
415  // Calculates the relevance score for the verbatim result from the
416  // default search engine.  This version takes into account context:
417  // i.e., whether the user has entered a keyword-based search or not.
418  int CalculateRelevanceForVerbatim() const;
419
420  // Calculates the relevance score for the verbatim result from the default
421  // search engine *ignoring* whether the input is a keyword-based search
422  // or not.  This function should only be used to determine the minimum
423  // relevance score that the best result from this provider should have.
424  // For normal use, prefer the above function.
425  int CalculateRelevanceForVerbatimIgnoringKeywordModeState() const;
426
427  // Gets the relevance score for the keyword verbatim result.
428  // |relevance_from_server| is handled as in GetVerbatimRelevance().
429  // TODO(mpearson): Refactor so this duplication isn't necesary or
430  // restructure so one static function takes all the parameters it needs
431  // (rather than looking at internal state).
432  int GetKeywordVerbatimRelevance(bool* relevance_from_server) const;
433
434  // |time| is the time at which this query was last seen.  |is_keyword|
435  // indicates whether the results correspond to the keyword provider or default
436  // provider. |use_aggressive_method| says whether this function can use a
437  // method that gives high scores (1200+) rather than one that gives lower
438  // scores.  When using the aggressive method, scores may exceed 1300
439  // unless |prevent_search_history_inlining| is set.
440  int CalculateRelevanceForHistory(const base::Time& time,
441                                   bool is_keyword,
442                                   bool use_aggressive_method,
443                                   bool prevent_search_history_inlining) const;
444
445  // Creates an AutocompleteMatch for "Search <engine> for |query_string|" with
446  // the supplied relevance.  Adds this match to |map|; if such a match already
447  // exists, whichever one has lower relevance is eliminated.
448  void AddMatchToMap(const string16& query_string,
449                     const string16& input_text,
450                     int relevance,
451                     bool relevance_from_server,
452                     AutocompleteMatch::Type type,
453                     int accepted_suggestion,
454                     bool is_keyword,
455                     MatchMap* map);
456
457  // Returns an AutocompleteMatch for a navigational suggestion.
458  AutocompleteMatch NavigationToMatch(const NavigationResult& navigation);
459
460  // Resets the scores of all |keyword_navigation_results_| matches to
461  // be below that of the top keyword query match (the verbatim match
462  // as expressed by |keyword_verbatim_relevance_| or keyword query
463  // suggestions stored in |keyword_suggest_results_|).  If there
464  // are no keyword suggestions and keyword verbatim is suppressed,
465  // then drops the suggested relevance scores for the navsuggestions
466  // and drops the request to suppress verbatim, thereby introducing the
467  // keyword verbatim match which will naturally outscore the navsuggestions.
468  void DemoteKeywordNavigationMatchesPastTopQuery();
469
470  // Updates the value of |done_| from the internal state.
471  void UpdateDone();
472
473  // The amount of time to wait before sending a new suggest request after the
474  // previous one.  Non-const because some unittests modify this value.
475  static int kMinimumTimeBetweenSuggestQueriesMs;
476
477  // We annotate our AutocompleteMatches with whether their relevance scores
478  // were server-provided using this key in the |additional_info| field.
479  static const char kRelevanceFromServerKey[];
480  // These are the values we record with the above key.
481  static const char kTrue[];
482  static const char kFalse[];
483
484  // Maintains the TemplateURLs used.
485  Providers providers_;
486
487  // The user's input.
488  AutocompleteInput input_;
489
490  // Input when searching against the keyword provider.
491  AutocompleteInput keyword_input_;
492
493  // Searches in the user's history that begin with the input text.
494  HistoryResults keyword_history_results_;
495  HistoryResults default_history_results_;
496
497  // Number of suggest results that haven't yet arrived. If greater than 0 it
498  // indicates one of the URLFetchers is still running.
499  int suggest_results_pending_;
500
501  // A timer to start a query to the suggest server after the user has stopped
502  // typing for long enough.
503  base::OneShotTimer<SearchProvider> timer_;
504
505  // The time at which we sent a query to the suggest server.
506  base::TimeTicks time_suggest_request_sent_;
507
508  // Fetchers used to retrieve results for the keyword and default providers.
509  scoped_ptr<net::URLFetcher> keyword_fetcher_;
510  scoped_ptr<net::URLFetcher> default_fetcher_;
511
512  // Results from the default and keyword search providers.
513  Results default_results_;
514  Results keyword_results_;
515
516  // Whether a field trial, if any, has triggered in the most recent
517  // autocomplete query.  This field is set to false in Start() and may be set
518  // to true if either the default provider or keyword provider has completed
519  // and their corresponding suggest response contained
520  // '"google:fieldtrialtriggered":true'.
521  // If the autocomplete query has not returned, this field is set to false.
522  bool field_trial_triggered_;
523
524  // Same as above except that it is maintained across the current Omnibox
525  // session.
526  bool field_trial_triggered_in_session_;
527
528  // If true, search history query suggestions will score low enough that
529  // they will not be inlined.
530  bool prevent_search_history_inlining_;
531
532  DISALLOW_COPY_AND_ASSIGN(SearchProvider);
533};
534
535#endif  // CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
536