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 #ifndef CHROME_BROWSER_AUTOCOMPLETE_HISTORY_URL_PROVIDER_H_ 6 #define CHROME_BROWSER_AUTOCOMPLETE_HISTORY_URL_PROVIDER_H_ 7 8 #include <string> 9 #include <vector> 10 11 #include "base/compiler_specific.h" 12 #include "base/synchronization/cancellation_flag.h" 13 #include "chrome/browser/autocomplete/autocomplete_input.h" 14 #include "chrome/browser/autocomplete/history_provider.h" 15 #include "chrome/browser/autocomplete/history_provider_util.h" 16 #include "chrome/browser/omnibox/omnibox_field_trial.h" 17 #include "chrome/browser/search_engines/template_url.h" 18 19 class Profile; 20 class SearchTermsData; 21 22 namespace base { 23 class MessageLoop; 24 } 25 26 namespace history { 27 class HistoryBackend; 28 class URLDatabase; 29 } 30 31 // How history autocomplete works 32 // ============================== 33 // 34 // Read down this diagram for temporal ordering. 35 // 36 // Main thread History thread 37 // ----------- -------------- 38 // AutocompleteController::Start 39 // -> HistoryURLProvider::Start 40 // -> SuggestExactInput 41 // [params_ allocated] 42 // -> DoAutocomplete (for inline autocomplete) 43 // -> URLDatabase::AutocompleteForPrefix (on in-memory DB) 44 // -> HistoryService::ScheduleAutocomplete 45 // (return to controller) ---- 46 // / 47 // HistoryBackend::ScheduleAutocomplete 48 // -> HistoryURLProvider::ExecuteWithDB 49 // -> DoAutocomplete 50 // -> URLDatabase::AutocompleteForPrefix 51 // / 52 // HistoryService::QueryComplete 53 // [params_ destroyed] 54 // -> AutocompleteProviderListener::OnProviderUpdate 55 // 56 // The autocomplete controller calls us, and must be called back, on the main 57 // thread. When called, we run two autocomplete passes. The first pass runs 58 // synchronously on the main thread and queries the in-memory URL database. 59 // This pass promotes matches for inline autocomplete if applicable. We do 60 // this synchronously so that users get consistent behavior when they type 61 // quickly and hit enter, no matter how loaded the main history database is. 62 // Doing this synchronously also prevents inline autocomplete from being 63 // "flickery" in the AutocompleteEdit. Because the in-memory DB does not have 64 // redirect data, results other than the top match might change between the 65 // two passes, so we can't just decide to use this pass' matches as the final 66 // results. 67 // 68 // The second autocomplete pass uses the full history database, which must be 69 // queried on the history thread. Start() asks the history service schedule to 70 // callback on the history thread with a pointer to the main database. When we 71 // are done doing queries, we schedule a task on the main thread that notifies 72 // the AutocompleteController that we're done. 73 // 74 // The communication between these threads is done using a 75 // HistoryURLProviderParams object. This is allocated in the main thread, and 76 // normally deleted in QueryComplete(). So that both autocomplete passes can 77 // use the same code, we also use this to hold results during the first 78 // autocomplete pass. 79 // 80 // While the second pass is running, the AutocompleteController may cancel the 81 // request. This can happen frequently when the user is typing quickly. In 82 // this case, the main thread sets params_->cancel, which the background thread 83 // checks periodically. If it finds the flag set, it stops what it's doing 84 // immediately and calls back to the main thread. (We don't delete the params 85 // on the history thread, because we should only do that when we can safely 86 // NULL out params_, and that must be done on the main thread.) 87 88 // Used to communicate autocomplete parameters between threads via the history 89 // service. 90 struct HistoryURLProviderParams { 91 // See comments on |promote_type| below. 92 enum PromoteType { 93 WHAT_YOU_TYPED_MATCH, 94 FRONT_HISTORY_MATCH, 95 NEITHER, 96 }; 97 98 HistoryURLProviderParams(const AutocompleteInput& input, 99 bool trim_http, 100 const AutocompleteMatch& what_you_typed_match, 101 const std::string& languages, 102 TemplateURL* default_search_provider, 103 const SearchTermsData& search_terms_data); 104 ~HistoryURLProviderParams(); 105 106 base::MessageLoop* message_loop; 107 108 // A copy of the autocomplete input. We need the copy since this object will 109 // live beyond the original query while it runs on the history thread. 110 AutocompleteInput input; 111 112 // Should inline autocompletion be disabled? This is initalized from 113 // |input.prevent_inline_autocomplete()|, but set to false is the input 114 // contains trailing white space. 115 bool prevent_inline_autocomplete; 116 117 // Set when "http://" should be trimmed from the beginning of the URLs. 118 bool trim_http; 119 120 // A match corresponding to what the user typed. 121 AutocompleteMatch what_you_typed_match; 122 123 // Set by the main thread to cancel this request. If this flag is set when 124 // the query runs, the query will be abandoned. This allows us to avoid 125 // running queries that are no longer needed. Since we don't care if we run 126 // the extra queries, the lack of signaling is not a problem. 127 base::CancellationFlag cancel_flag; 128 129 // Set by ExecuteWithDB() on the history thread when the query could not be 130 // performed because the history system failed to properly init the database. 131 // If this is set when the main thread is called back, it avoids changing 132 // |matches_| at all, so it won't delete the default match Start() creates. 133 bool failed; 134 135 // List of matches written by DoAutocomplete(). Upon its return the provider 136 // converts this list to ACMatches and places them in |matches_|. 137 history::HistoryMatches matches; 138 139 // True if the suggestion for exactly what the user typed appears as a known 140 // URL in the user's history. In this case, this will also be the first match 141 // in |matches|. 142 // 143 // NOTE: There are some complications related to keeping things consistent 144 // between passes and how we deal with intranet URLs, which are too complex to 145 // explain here; see the implementations of DoAutocomplete() and 146 // FixupExactSuggestion() for specific comments. 147 bool exact_suggestion_is_in_history; 148 149 // Tells the provider whether to promote the what you typed match, the first 150 // element of |matches|, or neither as the first AutocompleteMatch. If 151 // |exact_suggestion_is_in_history| is true (and thus "the what you typed 152 // match" and "the first element of |matches|" represent the same thing), this 153 // will be set to WHAT_YOU_TYPED_MATCH. 154 // 155 // NOTE: The second pass of DoAutocomplete() checks what the first pass set 156 // this to. See comments in DoAutocomplete(). 157 PromoteType promote_type; 158 159 // True if we should consider adding the what you typed match. This 160 // decision is often already summarized in |promote_type| by whether it 161 // says to promote the what you typed match. As such, this variable is 162 // only useful when |promote_type| is FRONT_HISTORY_MATCH. 163 bool have_what_you_typed_match; 164 165 // Languages we should pass to gfx::GetCleanStringFromUrl. 166 std::string languages; 167 168 // The default search provider and search terms data necessary to cull results 169 // that correspond to searches (on the default engine). These can only be 170 // obtained on the UI thread, so we have to copy them into here to pass them 171 // to the history thread. We use a scoped_ptr<TemplateURL> for the DSP since 172 // TemplateURLs can't be copied by value. We use a scoped_ptr<SearchTermsData> 173 // so that we can store a snapshot of the SearchTermsData accessible from the 174 // history thread. 175 scoped_ptr<TemplateURL> default_search_provider; 176 scoped_ptr<SearchTermsData> search_terms_data; 177 178 private: 179 DISALLOW_COPY_AND_ASSIGN(HistoryURLProviderParams); 180 }; 181 182 // This class is an autocomplete provider and is also a pseudo-internal 183 // component of the history system. See comments above. 184 class HistoryURLProvider : public HistoryProvider { 185 public: 186 // Various values used in scoring, made public so other providers 187 // can insert results in appropriate ranges relative to these. 188 static const int kScoreForBestInlineableResult; 189 static const int kScoreForUnvisitedIntranetResult; 190 static const int kScoreForWhatYouTypedResult; 191 static const int kBaseScoreForNonInlineableResult; 192 193 HistoryURLProvider(AutocompleteProviderListener* listener, Profile* profile); 194 195 // HistoryProvider: 196 virtual void Start(const AutocompleteInput& input, 197 bool minimal_changes) OVERRIDE; 198 virtual void Stop(bool clear_cached_results) OVERRIDE; 199 200 // Returns a match representing a navigation to |destination_url| given user 201 // input of |text|. |trim_http| controls whether the match's |fill_into_edit| 202 // and |contents| should have any HTTP scheme stripped off, and should not be 203 // set to true if |text| contains an http prefix. 204 // NOTES: This does not set the relevance of the returned match, as different 205 // callers want different behavior. Callers must set this manually. 206 // This function should only be called on the UI thread. 207 AutocompleteMatch SuggestExactInput(const base::string16& text, 208 const GURL& destination_url, 209 bool trim_http); 210 211 // Runs the history query on the history thread, called by the history 212 // system. The history database MAY BE NULL in which case it is not 213 // available and we should return no data. Also schedules returning the 214 // results to the main thread 215 void ExecuteWithDB(history::HistoryBackend* backend, 216 history::URLDatabase* db, 217 HistoryURLProviderParams* params); 218 219 private: 220 FRIEND_TEST_ALL_PREFIXES(HistoryURLProviderTest, HUPScoringExperiment); 221 222 enum MatchType { 223 NORMAL, 224 WHAT_YOU_TYPED, 225 INLINE_AUTOCOMPLETE, 226 UNVISITED_INTRANET, // An intranet site that has never been visited. 227 }; 228 class VisitClassifier; 229 230 ~HistoryURLProvider(); 231 232 // Determines the relevance for a match, given its type. If |match_type| is 233 // NORMAL, |match_number| is a number indicating the relevance of the match 234 // (higher == more relevant). For other values of |match_type|, 235 // |match_number| is ignored. Only called some of the time; for some matches, 236 // relevancy scores are assigned consecutively decreasing (1416, 1415, ...). 237 static int CalculateRelevance(MatchType match_type, int match_number); 238 239 // Returns a set of classifications that highlight all the occurrences of 240 // |input_text| at word breaks in |description|. 241 static ACMatchClassifications ClassifyDescription( 242 const base::string16& input_text, 243 const base::string16& description); 244 245 // Actually runs the autocomplete job on the given database, which is 246 // guaranteed not to be NULL. Used by both autocomplete passes, and therefore 247 // called on multiple different threads (though not simultaneously). 248 void DoAutocomplete(history::HistoryBackend* backend, 249 history::URLDatabase* db, 250 HistoryURLProviderParams* params); 251 252 // May promote either the what you typed match or first history match in 253 // params->matches to the front of |matches_|, depending on the value of 254 // params->promote_type. Also, depending on a field trial state, if it 255 // promotes the first history match, it may decide to append the what you 256 // typed matche immediately after. 257 void PromoteMatchesIfNecessary(const HistoryURLProviderParams& params); 258 259 // Dispatches the results to the autocomplete controller. Called on the 260 // main thread by ExecuteWithDB when the results are available. 261 // Frees params_gets_deleted on exit. 262 void QueryComplete(HistoryURLProviderParams* params_gets_deleted); 263 264 // Looks up the info for params->what_you_typed_match in the DB. If found, 265 // fills in the title, promotes the match's priority to that of an inline 266 // autocomplete match (maybe it should be slightly better?), and places it on 267 // the front of params->matches (so we pick the right matches to throw away 268 // when culling redirects to/from it). Returns whether a match was promoted. 269 bool FixupExactSuggestion(history::URLDatabase* db, 270 const VisitClassifier& classifier, 271 HistoryURLProviderParams* params) const; 272 273 // Helper function for FixupExactSuggestion, this returns true if the input 274 // corresponds to some intranet URL where the user has previously visited the 275 // host in question. In this case the input should be treated as a URL. 276 bool CanFindIntranetURL(history::URLDatabase* db, 277 const AutocompleteInput& input) const; 278 279 // Sees if a shorter version of the best match should be created, and if so 280 // places it at the front of params->matches. This can suggest history URLs 281 // that are prefixes of the best match (if they've been visited enough, 282 // compared to the best match), or create host-only suggestions even when they 283 // haven't been visited before: if the user visited http://example.com/asdf 284 // once, we'll suggest http://example.com/ even if they've never been to it. 285 // Returns true if a match was successfully created/promoted that we're 286 // willing to inline autocomplete. 287 bool PromoteOrCreateShorterSuggestion( 288 history::URLDatabase* db, 289 bool have_what_you_typed_match, 290 HistoryURLProviderParams* params); 291 292 // Removes results that have been rarely typed or visited, and not any time 293 // recently. The exact parameters for this heuristic can be found in the 294 // function body. Also culls results corresponding to queries from the default 295 // search engine. These are low-quality, difficult-to-understand matches for 296 // users, and the SearchProvider should surface past queries in a better way 297 // anyway. 298 void CullPoorMatches(HistoryURLProviderParams* params) const; 299 300 // Removes results that redirect to each other, leaving at most |max_results| 301 // results. 302 void CullRedirects(history::HistoryBackend* backend, 303 history::HistoryMatches* matches, 304 size_t max_results) const; 305 306 // Helper function for CullRedirects, this removes all but the first 307 // occurance of [any of the set of strings in |remove|] from the |matches| 308 // list. 309 // 310 // The return value is the index of the item that is after the item in the 311 // input identified by |source_index|. If |source_index| or an item before 312 // is removed, the next item will be shifted, and this allows the caller to 313 // pick up on the next one when this happens. 314 size_t RemoveSubsequentMatchesOf(history::HistoryMatches* matches, 315 size_t source_index, 316 const std::vector<GURL>& remove) const; 317 318 // Converts a specified |match_number| from params.matches into an 319 // autocomplete match for display. If experimental scoring is enabled, the 320 // final relevance score might be different from the given |relevance|. 321 // NOTE: This function should only be called on the UI thread. 322 AutocompleteMatch HistoryMatchToACMatch( 323 const HistoryURLProviderParams& params, 324 size_t match_number, 325 MatchType match_type, 326 int relevance); 327 328 // Params for the current query. The provider should not free this directly; 329 // instead, it is passed as a parameter through the history backend, and the 330 // parameter itself is freed once it's no longer needed. The only reason we 331 // keep this member is so we can set the cancel bit on it. 332 HistoryURLProviderParams* params_; 333 334 // Params controlling experimental behavior of this provider. 335 HUPScoringParams scoring_params_; 336 337 // If true, HistoryURL provider should lookup and cull redirects. If 338 // false, it returns matches that may be redirects to each other and 339 // simply hopes the default AutoCompleteController behavior to remove 340 // URLs that are likely duplicates (http://google.com <-> 341 // https://www.google.com/, etc.) will do a good enough job. 342 bool cull_redirects_; 343 344 // Used in PromoteOrCreateShorterSuggestion(). If true, we may create 345 // shorter suggestions even when they haven't been visited before: 346 // if the user visited http://example.com/asdf once, we'll suggest 347 // http://example.com/ even if they've never been to it. 348 bool create_shorter_match_; 349 350 DISALLOW_COPY_AND_ASSIGN(HistoryURLProvider); 351 }; 352 353 #endif // CHROME_BROWSER_AUTOCOMPLETE_HISTORY_URL_PROVIDER_H_ 354