search_provider.h revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 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/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
31class Profile;
32class 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.
44class 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 std::wstring& input_text,
63                            const std::wstring& 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 std::wstring& 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    std::wstring site_name;
165  };
166
167  typedef std::vector<std::wstring> SuggestResults;
168  typedef std::vector<NavigationResult> NavigationResults;
169  typedef std::vector<history::KeywordSearchTermVisit> HistoryResults;
170  typedef std::map<std::wstring, 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 std::wstring& 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 std::wstring& 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.
237  int CalculateRelevanceForHistory(const base::Time& time,
238                                   bool is_keyword) const;
239  // |result_number| is the index of the suggestion in the result set from the
240  // server; the best suggestion is suggestion number 0.  |is_keyword| is true
241  // if the search is from the keyword provider.
242  int CalculateRelevanceForSuggestion(size_t num_results,
243                                      size_t result_number,
244                                      bool is_keyword) const;
245  // |result_number| is same as above. |is_keyword| is true if the navigation
246  // result was suggested by the keyword provider.
247  int CalculateRelevanceForNavigation(size_t num_results,
248                                      size_t result_number,
249                                      bool is_keyword) const;
250
251  // Creates an AutocompleteMatch for "Search <engine> for |query_string|" with
252  // the supplied relevance.  Adds this match to |map|; if such a match already
253  // exists, whichever one has lower relevance is eliminated.
254  void AddMatchToMap(const std::wstring& query_string,
255                     const std::wstring& input_text,
256                     int relevance,
257                     AutocompleteMatch::Type type,
258                     int accepted_suggestion,
259                     bool is_keyword,
260                     bool prevent_inline_autocomplete,
261                     MatchMap* map);
262
263  // Returns an AutocompleteMatch for a navigational suggestion.
264  AutocompleteMatch NavigationToMatch(const NavigationResult& query_string,
265                                      int relevance,
266                                      bool is_keyword);
267
268  // Updates the value of |done_| from the internal state.
269  void UpdateDone();
270
271  // Updates the description/description_class of the first search match.
272  void UpdateFirstSearchMatchDescription();
273
274  // Should we query for suggest results immediately? This is normally false,
275  // but may be set to true during testing.
276  static bool query_suggest_immediately_;
277
278  // Maintains the TemplateURLs used.
279  Providers providers_;
280
281  // The user's input.
282  AutocompleteInput input_;
283
284  // Input text when searching against the keyword provider.
285  std::wstring keyword_input_text_;
286
287  // Searches in the user's history that begin with the input text.
288  HistoryResults keyword_history_results_;
289  HistoryResults default_history_results_;
290
291  // Number of suggest results that haven't yet arrived. If greater than 0 it
292  // indicates either |timer_| or one of the URLFetchers is still running.
293  int suggest_results_pending_;
294
295  // A timer to start a query to the suggest server after the user has stopped
296  // typing for long enough.
297  base::OneShotTimer<SearchProvider> timer_;
298
299  // The fetcher that retrieves suggest results for the keyword from the server.
300  scoped_ptr<URLFetcher> keyword_fetcher_;
301
302  // The fetcher that retrieves suggest results for the default engine from the
303  // server.
304  scoped_ptr<URLFetcher> default_fetcher_;
305
306  // Suggestions returned by the Suggest server for the input text.
307  SuggestResults keyword_suggest_results_;
308  SuggestResults default_suggest_results_;
309
310  // Navigational suggestions returned by the server.
311  NavigationResults keyword_navigation_results_;
312  NavigationResults default_navigation_results_;
313
314  // Whether suggest_results_ is valid.
315  bool have_suggest_results_;
316
317  // Has FinalizeInstantQuery been invoked since the last |Start|?
318  bool instant_finalized_;
319
320  DISALLOW_COPY_AND_ASSIGN(SearchProvider);
321};
322
323#endif  // CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
324