Home | History | Annotate | Download | only in autocomplete
      1 // Copyright (c) 2011 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 non-keyword autocomplete entries that start with
      7 // "Search <engine> for ...", including searching for the current input string,
      8 // search history, and search suggestions.  An instance of it gets created and
      9 // managed by the autocomplete controller.
     10 //
     11 // For more information on the autocomplete system in general, including how
     12 // the autocomplete controller and autocomplete providers work, see
     13 // chrome/browser/autocomplete.h.
     14 
     15 #ifndef CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
     16 #define CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
     17 #pragma once
     18 
     19 #include <map>
     20 #include <string>
     21 #include <vector>
     22 
     23 #include "base/memory/scoped_ptr.h"
     24 #include "chrome/browser/autocomplete/autocomplete.h"
     25 #include "chrome/browser/autocomplete/autocomplete_match.h"
     26 #include "chrome/browser/history/history_types.h"
     27 #include "chrome/browser/search_engines/template_url.h"
     28 #include "chrome/browser/search_engines/template_url_id.h"
     29 #include "chrome/common/net/url_fetcher.h"
     30 
     31 class Profile;
     32 class Value;
     33 
     34 // Autocomplete provider for searches and suggestions from a search engine.
     35 //
     36 // After construction, the autocomplete controller repeatedly calls Start()
     37 // with some user input, each time expecting to receive a small set of the best
     38 // matches (either synchronously or asynchronously).
     39 //
     40 // Initially the provider creates a match that searches for the current input
     41 // text.  It also starts a task to query the Suggest servers.  When that data
     42 // comes back, the provider creates and returns matches for the best
     43 // suggestions.
     44 class SearchProvider : public AutocompleteProvider,
     45                        public URLFetcher::Delegate {
     46  public:
     47   SearchProvider(ACProviderListener* listener, Profile* profile);
     48 
     49 #if defined(UNIT_TEST)
     50   static void set_query_suggest_immediately(bool value) {
     51     query_suggest_immediately_ = value;
     52   }
     53 #endif
     54 
     55   // Marks the instant query as done. If |input_text| is non-empty this changes
     56   // the 'search what you typed' results text to |input_text| + |suggest_text|.
     57   // |input_text| is the text the user input into the edit. |input_text| differs
     58   // from |input_.text()| if the input contained whitespace.
     59   //
     60   // This method also marks the search provider as no longer needing to wait for
     61   // the instant result.
     62   void FinalizeInstantQuery(const string16& input_text,
     63                             const string16& suggest_text);
     64 
     65   // AutocompleteProvider
     66   virtual void Start(const AutocompleteInput& input,
     67                      bool minimal_changes);
     68   virtual void Stop();
     69 
     70   // URLFetcher::Delegate
     71   virtual void OnURLFetchComplete(const URLFetcher* source,
     72                                   const GURL& url,
     73                                   const net::URLRequestStatus& status,
     74                                   int response_code,
     75                                   const ResponseCookies& cookies,
     76                                   const std::string& data);
     77 
     78   // ID used in creating URLFetcher for default provider's suggest results.
     79   static const int kDefaultProviderURLFetcherID;
     80 
     81   // ID used in creating URLFetcher for keyword provider's suggest results.
     82   static const int kKeywordProviderURLFetcherID;
     83 
     84  private:
     85   ~SearchProvider();
     86 
     87   // Manages the providers (TemplateURLs) used by SearchProvider. Two providers
     88   // may be used:
     89   // . The default provider. This corresponds to the user's default search
     90   //   engine. This is always used, except for the rare case of no default
     91   //   engine.
     92   // . The keyword provider. This is used if the user has typed in a keyword.
     93   class Providers {
     94    public:
     95     Providers() : default_provider_(NULL), keyword_provider_(NULL) {}
     96 
     97     // Returns true if the specified providers match the two providers managed
     98     // by this class.
     99     bool equals(const TemplateURL* default_provider,
    100                 const TemplateURL* keyword_provider) {
    101       return (default_provider == default_provider_ &&
    102               keyword_provider == keyword_provider_);
    103     }
    104 
    105     // Resets the providers.
    106     void Set(const TemplateURL* default_provider,
    107              const TemplateURL* keyword_provider);
    108 
    109     const TemplateURL& default_provider() const {
    110       DCHECK(valid_default_provider());
    111       return cached_default_provider_;
    112     }
    113 
    114     const TemplateURL& keyword_provider() const {
    115       DCHECK(valid_keyword_provider());
    116       return cached_keyword_provider_;
    117     }
    118 
    119     // Returns true of the keyword provider is valid.
    120     bool valid_keyword_provider() const { return !!keyword_provider_; }
    121 
    122     // Returns true if the keyword provider is valid and has a valid suggest
    123     // url.
    124     bool valid_suggest_for_keyword_provider() const {
    125       return keyword_provider_ && cached_keyword_provider_.suggestions_url();
    126     }
    127 
    128     // Returns true of the default provider is valid.
    129     bool valid_default_provider() const { return !!default_provider_; }
    130 
    131     // Returns true if the default provider is valid and has a valid suggest
    132     // url.
    133     bool valid_suggest_for_default_provider() const {
    134       return default_provider_ && cached_default_provider_.suggestions_url();
    135     }
    136 
    137     // Returns true if |from_keyword_provider| is true, or
    138     // the keyword provider is not valid.
    139     bool is_primary_provider(bool from_keyword_provider) const {
    140       return from_keyword_provider || !valid_keyword_provider();
    141     }
    142 
    143    private:
    144     // Cached across the life of a query so we behave consistently even if the
    145     // user changes their default while the query is running.
    146     TemplateURL cached_default_provider_;
    147     TemplateURL cached_keyword_provider_;
    148 
    149     // TODO(pkasting): http://b/1162970  We shouldn't need these.
    150     const TemplateURL* default_provider_;
    151     const TemplateURL* keyword_provider_;
    152   };
    153 
    154   struct NavigationResult {
    155     NavigationResult(const GURL& url, const string16& site_name)
    156         : url(url),
    157           site_name(site_name) {
    158     }
    159 
    160     // The URL.
    161     GURL url;
    162 
    163     // Name for the site.
    164     string16 site_name;
    165   };
    166 
    167   typedef std::vector<string16> SuggestResults;
    168   typedef std::vector<NavigationResult> NavigationResults;
    169   typedef std::vector<history::KeywordSearchTermVisit> HistoryResults;
    170   typedef std::map<string16, AutocompleteMatch> MatchMap;
    171 
    172   // Called when timer_ expires.
    173   void Run();
    174 
    175   // Runs the history query, if necessary. The history query is synchronous.
    176   // This does not update |done_|.
    177   void DoHistoryQuery(bool minimal_changes);
    178 
    179   // Determines whether an asynchronous subcomponent query should run for the
    180   // current input.  If so, starts it if necessary; otherwise stops it.
    181   // NOTE: This function does not update |done_|.  Callers must do so.
    182   void StartOrStopSuggestQuery(bool minimal_changes);
    183 
    184   // Returns true when the current query can be sent to the Suggest service.
    185   // This will be false e.g. when Suggest is disabled, the query contains
    186   // potentially private data, etc.
    187   bool IsQuerySuitableForSuggest() const;
    188 
    189   // Stops the suggest query.
    190   // NOTE: This does not update |done_|.  Callers must do so.
    191   void StopSuggest();
    192 
    193   // Creates a URLFetcher requesting suggest results for the specified
    194   // TemplateURL. Ownership of the returned URLFetchet passes to the caller.
    195   URLFetcher* CreateSuggestFetcher(int id,
    196                                    const TemplateURL& provider,
    197                                    const string16& text);
    198 
    199   // Parses the results from the Suggest server and stores up to kMaxMatches of
    200   // them in server_results_.  Returns whether parsing succeeded.
    201   bool ParseSuggestResults(Value* root_val,
    202                            bool is_keyword,
    203                            const string16& input_text,
    204                            SuggestResults* suggest_results);
    205 
    206   // Converts the parsed server results in server_results_ to a set of
    207   // AutocompleteMatches and adds them to |matches_|.  This also sets |done_|
    208   // correctly.
    209   void ConvertResultsToAutocompleteMatches();
    210 
    211   // Converts the first navigation result in |navigation_results| to an
    212   // AutocompleteMatch and adds it to |matches_|.
    213   void AddNavigationResultsToMatches(
    214     const NavigationResults& navigation_results,
    215     bool is_keyword);
    216 
    217   // Adds a match for each result in |results| to |map|. |is_keyword| indicates
    218   // whether the results correspond to the keyword provider or default provider.
    219   void AddHistoryResultsToMap(const HistoryResults& results,
    220                               bool is_keyword,
    221                               int did_not_accept_suggestion,
    222                               MatchMap* map);
    223 
    224   // Adds a match for each result in |suggest_results| to |map|. |is_keyword|
    225   // indicates whether the results correspond to the keyword provider or default
    226   // provider.
    227   void AddSuggestResultsToMap(const SuggestResults& suggest_results,
    228                               bool is_keyword,
    229                               int did_not_accept_suggestion,
    230                               MatchMap* map);
    231 
    232   // Determines the relevance for a particular match.  We use different scoring
    233   // algorithms for the different types of matches.
    234   int CalculateRelevanceForWhatYouTyped() const;
    235   // |time| is the time at which this query was last seen. |is_keyword| is true
    236   // if the search is from the keyword provider. |looks_like_url| is true if the
    237   // search term would be treated as a URL if typed into the omnibox.
    238   int CalculateRelevanceForHistory(const base::Time& time,
    239                                    bool looks_like_url,
    240                                    bool is_keyword) const;
    241   // |result_number| is the index of the suggestion in the result set from the
    242   // server; the best suggestion is suggestion number 0.  |is_keyword| is true
    243   // if the search is from the keyword provider.
    244   int CalculateRelevanceForSuggestion(size_t num_results,
    245                                       size_t result_number,
    246                                       bool is_keyword) const;
    247   // |result_number| is same as above. |is_keyword| is true if the navigation
    248   // result was suggested by the keyword provider.
    249   int CalculateRelevanceForNavigation(size_t num_results,
    250                                       size_t result_number,
    251                                       bool is_keyword) const;
    252 
    253   // Creates an AutocompleteMatch for "Search <engine> for |query_string|" with
    254   // the supplied relevance.  Adds this match to |map|; if such a match already
    255   // exists, whichever one has lower relevance is eliminated.
    256   void AddMatchToMap(const string16& query_string,
    257                      const string16& input_text,
    258                      int relevance,
    259                      AutocompleteMatch::Type type,
    260                      int accepted_suggestion,
    261                      bool is_keyword,
    262                      bool prevent_inline_autocomplete,
    263                      MatchMap* map);
    264 
    265   // Returns an AutocompleteMatch for a navigational suggestion.
    266   AutocompleteMatch NavigationToMatch(const NavigationResult& query_string,
    267                                       int relevance,
    268                                       bool is_keyword);
    269 
    270   // Updates the value of |done_| from the internal state.
    271   void UpdateDone();
    272 
    273   // Updates the description/description_class of the first search match.
    274   void UpdateFirstSearchMatchDescription();
    275 
    276   // Should we query for suggest results immediately? This is normally false,
    277   // but may be set to true during testing.
    278   static bool query_suggest_immediately_;
    279 
    280   // Maintains the TemplateURLs used.
    281   Providers providers_;
    282 
    283   // The user's input.
    284   AutocompleteInput input_;
    285 
    286   // Input text when searching against the keyword provider.
    287   string16 keyword_input_text_;
    288 
    289   // Searches in the user's history that begin with the input text.
    290   HistoryResults keyword_history_results_;
    291   HistoryResults default_history_results_;
    292 
    293   // Number of suggest results that haven't yet arrived. If greater than 0 it
    294   // indicates either |timer_| or one of the URLFetchers is still running.
    295   int suggest_results_pending_;
    296 
    297   // A timer to start a query to the suggest server after the user has stopped
    298   // typing for long enough.
    299   base::OneShotTimer<SearchProvider> timer_;
    300 
    301   // The fetcher that retrieves suggest results for the keyword from the server.
    302   scoped_ptr<URLFetcher> keyword_fetcher_;
    303 
    304   // The fetcher that retrieves suggest results for the default engine from the
    305   // server.
    306   scoped_ptr<URLFetcher> default_fetcher_;
    307 
    308   // Suggestions returned by the Suggest server for the input text.
    309   SuggestResults keyword_suggest_results_;
    310   SuggestResults default_suggest_results_;
    311 
    312   // Navigational suggestions returned by the server.
    313   NavigationResults keyword_navigation_results_;
    314   NavigationResults default_navigation_results_;
    315 
    316   // Whether suggest_results_ is valid.
    317   bool have_suggest_results_;
    318 
    319   // Has FinalizeInstantQuery been invoked since the last |Start|?
    320   bool instant_finalized_;
    321 
    322   // The |suggest_text| parameter passed to FinalizeInstantQuery.
    323   string16 default_provider_suggest_text_;
    324 
    325   DISALLOW_COPY_AND_ASSIGN(SearchProvider);
    326 };
    327 
    328 #endif  // CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
    329