Home | History | Annotate | Download | only in autocomplete
      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 
     30 class Profile;
     31 class SearchProviderTest;
     32 class TemplateURLService;
     33 
     34 namespace base {
     35 class Value;
     36 }
     37 
     38 namespace net {
     39 class 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.
     52 class 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