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