// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef COMPONENTS_OMNIBOX_BROWSER_HISTORY_URL_PROVIDER_H_ #define COMPONENTS_OMNIBOX_BROWSER_HISTORY_URL_PROVIDER_H_ #include #include #include "base/compiler_specific.h" #include "base/synchronization/cancellation_flag.h" #include "base/threading/thread_checker.h" #include "components/history/core/browser/history_match.h" #include "components/omnibox/browser/autocomplete_input.h" #include "components/omnibox/browser/history_provider.h" #include "components/omnibox/browser/omnibox_field_trial.h" #include "components/search_engines/template_url.h" class AutocompleteProviderListener; class SearchTermsData; namespace base { class MessageLoop; } namespace history { class HistoryBackend; class URLDatabase; } // How history autocomplete works // ============================== // // Read down this diagram for temporal ordering. // // Main thread History thread // ----------- -------------- // AutocompleteController::Start // -> HistoryURLProvider::Start // -> SuggestExactInput // [params_ allocated] // -> DoAutocomplete (for inline autocomplete) // -> URLDatabase::AutocompleteForPrefix (on in-memory DB) // -> HistoryService::ScheduleAutocomplete // (return to controller) ---- // / // HistoryBackend::ScheduleAutocomplete // -> HistoryURLProvider::ExecuteWithDB // -> DoAutocomplete // -> URLDatabase::AutocompleteForPrefix // / // HistoryService::QueryComplete // [params_ destroyed] // -> AutocompleteProviderListener::OnProviderUpdate // // The autocomplete controller calls us, and must be called back, on the main // thread. When called, we run two autocomplete passes. The first pass runs // synchronously on the main thread and queries the in-memory URL database. // This pass promotes matches for inline autocomplete if applicable. We do // this synchronously so that users get consistent behavior when they type // quickly and hit enter, no matter how loaded the main history database is. // Doing this synchronously also prevents inline autocomplete from being // "flickery" in the AutocompleteEdit. Because the in-memory DB does not have // redirect data, results other than the top match might change between the // two passes, so we can't just decide to use this pass' matches as the final // results. // // The second autocomplete pass uses the full history database, which must be // queried on the history thread. Start() asks the history service schedule to // callback on the history thread with a pointer to the main database. When we // are done doing queries, we schedule a task on the main thread that notifies // the AutocompleteController that we're done. // // The communication between these threads is done using a // HistoryURLProviderParams object. This is allocated in the main thread, and // normally deleted in QueryComplete(). So that both autocomplete passes can // use the same code, we also use this to hold results during the first // autocomplete pass. // // While the second pass is running, the AutocompleteController may cancel the // request. This can happen frequently when the user is typing quickly. In // this case, the main thread sets params_->cancel, which the background thread // checks periodically. If it finds the flag set, it stops what it's doing // immediately and calls back to the main thread. (We don't delete the params // on the history thread, because we should only do that when we can safely // NULL out params_, and that must be done on the main thread.) // Used to communicate autocomplete parameters between threads via the history // service. struct HistoryURLProviderParams { // See comments on |promote_type| below. enum PromoteType { WHAT_YOU_TYPED_MATCH, FRONT_HISTORY_MATCH, NEITHER, }; HistoryURLProviderParams(const AutocompleteInput& input, bool trim_http, const AutocompleteMatch& what_you_typed_match, const std::string& languages, TemplateURL* default_search_provider, const SearchTermsData& search_terms_data); ~HistoryURLProviderParams(); base::MessageLoop* message_loop; // A copy of the autocomplete input. We need the copy since this object will // live beyond the original query while it runs on the history thread. AutocompleteInput input; // Should inline autocompletion be disabled? This is initalized from // |input.prevent_inline_autocomplete()|, but set to false is the input // contains trailing white space. bool prevent_inline_autocomplete; // Set when "http://" should be trimmed from the beginning of the URLs. bool trim_http; // A match corresponding to what the user typed. AutocompleteMatch what_you_typed_match; // Set by the main thread to cancel this request. If this flag is set when // the query runs, the query will be abandoned. This allows us to avoid // running queries that are no longer needed. Since we don't care if we run // the extra queries, the lack of signaling is not a problem. base::CancellationFlag cancel_flag; // Set by ExecuteWithDB() on the history thread when the query could not be // performed because the history system failed to properly init the database. // If this is set when the main thread is called back, it avoids changing // |matches_| at all, so it won't delete the default match Start() creates. bool failed; // List of matches written by DoAutocomplete(). Upon its return the provider // converts this list to ACMatches and places them in |matches_|. history::HistoryMatches matches; // True if the suggestion for exactly what the user typed appears as a known // URL in the user's history. In this case, this will also be the first match // in |matches|. // // NOTE: There are some complications related to keeping things consistent // between passes and how we deal with intranet URLs, which are too complex to // explain here; see the implementations of DoAutocomplete() and // FixupExactSuggestion() for specific comments. bool exact_suggestion_is_in_history; // Tells the provider whether to promote the what you typed match, the first // element of |matches|, or neither as the first AutocompleteMatch. If // |exact_suggestion_is_in_history| is true (and thus "the what you typed // match" and "the first element of |matches|" represent the same thing), this // will be set to WHAT_YOU_TYPED_MATCH. // // NOTE: The second pass of DoAutocomplete() checks what the first pass set // this to. See comments in DoAutocomplete(). PromoteType promote_type; // True if |what_you_typed_match| is eligible for display. If this is true, // PromoteMatchesIfNecessary() may choose to place |what_you_typed_match| on // |matches_| even when |promote_type| is not WHAT_YOU_TYPED_MATCH. bool have_what_you_typed_match; // Languages we should pass to gfx::GetCleanStringFromUrl. std::string languages; // The default search provider and search terms data necessary to cull results // that correspond to searches (on the default engine). These can only be // obtained on the UI thread, so we have to copy them into here to pass them // to the history thread. We use a scoped_ptr for the DSP since // TemplateURLs can't be copied by value. We use a scoped_ptr // so that we can store a snapshot of the SearchTermsData accessible from the // history thread. scoped_ptr default_search_provider; scoped_ptr search_terms_data; private: DISALLOW_COPY_AND_ASSIGN(HistoryURLProviderParams); }; // This class is an autocomplete provider and is also a pseudo-internal // component of the history system. See comments above. class HistoryURLProvider : public HistoryProvider { public: // Various values used in scoring, made public so other providers // can insert results in appropriate ranges relative to these. static const int kScoreForBestInlineableResult; static const int kScoreForUnvisitedIntranetResult; static const int kScoreForWhatYouTypedResult; static const int kBaseScoreForNonInlineableResult; HistoryURLProvider(AutocompleteProviderClient* client, AutocompleteProviderListener* listener); // HistoryProvider: void Start(const AutocompleteInput& input, bool minimal_changes) override; void Stop(bool clear_cached_results, bool due_to_user_inactivity) override; // Returns a match representing a navigation to |destination_url|, highlighted // appropriately against |input|. |trim_http| controls whether the match's // |fill_into_edit| and |contents| should have any HTTP stripped off, and // should not be set to true if the user's original input contains an http // prefix. // NOTES: This does not set the relevance of the returned match, as different // callers want different behavior. Callers must set this manually. // This function should only be called on the UI thread. AutocompleteMatch SuggestExactInput(const AutocompleteInput& input, const GURL& destination_url, bool trim_http); // Runs the history query on the history thread, called by the history // system. The history database MAY BE NULL in which case it is not // available and we should return no data. Also schedules returning the // results to the main thread void ExecuteWithDB(HistoryURLProviderParams* params, history::HistoryBackend* backend, history::URLDatabase* db); private: FRIEND_TEST_ALL_PREFIXES(HistoryURLProviderTest, HUPScoringExperiment); enum MatchType { NORMAL, WHAT_YOU_TYPED, INLINE_AUTOCOMPLETE, UNVISITED_INTRANET, // An intranet site that has never been visited. }; class VisitClassifier; ~HistoryURLProvider() override; // Determines the relevance for a match, given its type. If |match_type| is // NORMAL, |match_number| is a number indicating the relevance of the match // (higher == more relevant). For other values of |match_type|, // |match_number| is ignored. Only called some of the time; for some matches, // relevancy scores are assigned consecutively decreasing (1416, 1415, ...). static int CalculateRelevance(MatchType match_type, int match_number); // Returns a set of classifications that highlight all the occurrences of // |input_text| at word breaks in |description|. static ACMatchClassifications ClassifyDescription( const base::string16& input_text, const base::string16& description); // Actually runs the autocomplete job on the given database, which is // guaranteed not to be NULL. Used by both autocomplete passes, and therefore // called on multiple different threads (though not simultaneously). void DoAutocomplete(history::HistoryBackend* backend, history::URLDatabase* db, HistoryURLProviderParams* params); // May promote the what you typed match, the first history match in // params->matches, or both to the front of |matches_|, depending on the // values of params->promote_type, params->have_what_you_typed_match, and // params->prevent_inline_autocomplete. void PromoteMatchesIfNecessary(const HistoryURLProviderParams& params); // Dispatches the results to the autocomplete controller. Called on the // main thread by ExecuteWithDB when the results are available. // Frees params_gets_deleted on exit. void QueryComplete(HistoryURLProviderParams* params_gets_deleted); // Looks up the info for params->what_you_typed_match in the DB. If found, // fills in the title, promotes the match's priority to that of an inline // autocomplete match (maybe it should be slightly better?), and places it on // the front of params->matches (so we pick the right matches to throw away // when culling redirects to/from it). Returns whether a match was promoted. bool FixupExactSuggestion(history::URLDatabase* db, const VisitClassifier& classifier, HistoryURLProviderParams* params) const; // Helper function for FixupExactSuggestion, this returns true if the input // corresponds to some intranet URL where the user has previously visited the // host in question. In this case the input should be treated as a URL. bool CanFindIntranetURL(history::URLDatabase* db, const AutocompleteInput& input) const; // Sees if a shorter version of the best match should be created, and if so // places it at the front of params->matches. This can suggest history URLs // that are prefixes of the best match (if they've been visited enough, // compared to the best match), or create host-only suggestions even when they // haven't been visited before: if the user visited http://example.com/asdf // once, we'll suggest http://example.com/ even if they've never been to it. // Returns true if a match was successfully created/promoted that we're // willing to inline autocomplete. bool PromoteOrCreateShorterSuggestion( history::URLDatabase* db, HistoryURLProviderParams* params); // Removes results that have been rarely typed or visited, and not any time // recently. The exact parameters for this heuristic can be found in the // function body. Also culls results corresponding to queries from the default // search engine. These are low-quality, difficult-to-understand matches for // users, and the SearchProvider should surface past queries in a better way // anyway. void CullPoorMatches(HistoryURLProviderParams* params) const; // Removes results that redirect to each other, leaving at most |max_results| // results. void CullRedirects(history::HistoryBackend* backend, history::HistoryMatches* matches, size_t max_results) const; // Helper function for CullRedirects, this removes all but the first // occurance of [any of the set of strings in |remove|] from the |matches| // list. // // The return value is the index of the item that is after the item in the // input identified by |source_index|. If |source_index| or an item before // is removed, the next item will be shifted, and this allows the caller to // pick up on the next one when this happens. size_t RemoveSubsequentMatchesOf(history::HistoryMatches* matches, size_t source_index, const std::vector& remove) const; // Converts a specified |match_number| from params.matches into an // autocomplete match for display. If experimental scoring is enabled, the // final relevance score might be different from the given |relevance|. // NOTE: This function should only be called on the UI thread. AutocompleteMatch HistoryMatchToACMatch( const HistoryURLProviderParams& params, size_t match_number, MatchType match_type, int relevance); AutocompleteProviderListener* listener_; // Params for the current query. The provider should not free this directly; // instead, it is passed as a parameter through the history backend, and the // parameter itself is freed once it's no longer needed. The only reason we // keep this member is so we can set the cancel bit on it. HistoryURLProviderParams* params_; // Params controlling experimental behavior of this provider. HUPScoringParams scoring_params_; base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(HistoryURLProvider); }; #endif // COMPONENTS_OMNIBOX_BROWSER_HISTORY_URL_PROVIDER_H_