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) set_query_suggest_immediately(bool value)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: Providers()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. equals(const TemplateURL * default_provider,const TemplateURL * keyword_provider)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 default_provider()109 const TemplateURL& default_provider() const { 110 DCHECK(valid_default_provider()); 111 return cached_default_provider_; 112 } 113 keyword_provider()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. valid_keyword_provider()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. valid_suggest_for_keyword_provider()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. valid_default_provider()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. valid_suggest_for_default_provider()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. is_primary_provider(bool from_keyword_provider)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 { NavigationResultNavigationResult155 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