// Copyright (c) 2011 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.
//
// This file contains the Search autocomplete provider. This provider is
// responsible for all non-keyword autocomplete entries that start with
// "Search <engine> for ...", including searching for the current input string,
// search history, and search suggestions. An instance of it gets created and
// managed by the autocomplete controller.
//
// For more information on the autocomplete system in general, including how
// the autocomplete controller and autocomplete providers work, see
// chrome/browser/autocomplete.h.
#ifndef CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
#define CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_
#pragma once
#include <map>
#include <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/autocomplete/autocomplete.h"
#include "chrome/browser/autocomplete/autocomplete_match.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_id.h"
#include "chrome/common/net/url_fetcher.h"
class Profile;
class Value;
// Autocomplete provider for searches and suggestions from a search engine.
//
// After construction, the autocomplete controller repeatedly calls Start()
// with some user input, each time expecting to receive a small set of the best
// matches (either synchronously or asynchronously).
//
// Initially the provider creates a match that searches for the current input
// text. It also starts a task to query the Suggest servers. When that data
// comes back, the provider creates and returns matches for the best
// suggestions.
class SearchProvider : public AutocompleteProvider,
public URLFetcher::Delegate {
public:
SearchProvider(ACProviderListener* listener, Profile* profile);
#if defined(UNIT_TEST)
static void set_query_suggest_immediately(bool value) {
query_suggest_immediately_ = value;
}
#endif
// Marks the instant query as done. If |input_text| is non-empty this changes
// the 'search what you typed' results text to |input_text| + |suggest_text|.
// |input_text| is the text the user input into the edit. |input_text| differs
// from |input_.text()| if the input contained whitespace.
//
// This method also marks the search provider as no longer needing to wait for
// the instant result.
void FinalizeInstantQuery(const string16& input_text,
const string16& suggest_text);
// AutocompleteProvider
virtual void Start(const AutocompleteInput& input,
bool minimal_changes);
virtual void Stop();
// URLFetcher::Delegate
virtual void OnURLFetchComplete(const URLFetcher* source,
const GURL& url,
const net::URLRequestStatus& status,
int response_code,
const ResponseCookies& cookies,
const std::string& data);
// ID used in creating URLFetcher for default provider's suggest results.
static const int kDefaultProviderURLFetcherID;
// ID used in creating URLFetcher for keyword provider's suggest results.
static const int kKeywordProviderURLFetcherID;
private:
~SearchProvider();
// Manages the providers (TemplateURLs) used by SearchProvider. Two providers
// may be used:
// . The default provider. This corresponds to the user's default search
// engine. This is always used, except for the rare case of no default
// engine.
// . The keyword provider. This is used if the user has typed in a keyword.
class Providers {
public:
Providers() : default_provider_(NULL), keyword_provider_(NULL) {}
// Returns true if the specified providers match the two providers managed
// by this class.
bool equals(const TemplateURL* default_provider,
const TemplateURL* keyword_provider) {
return (default_provider == default_provider_ &&
keyword_provider == keyword_provider_);
}
// Resets the providers.
void Set(const TemplateURL* default_provider,
const TemplateURL* keyword_provider);
const TemplateURL& default_provider() const {
DCHECK(valid_default_provider());
return cached_default_provider_;
}
const TemplateURL& keyword_provider() const {
DCHECK(valid_keyword_provider());
return cached_keyword_provider_;
}
// Returns true of the keyword provider is valid.
bool valid_keyword_provider() const { return !!keyword_provider_; }
// Returns true if the keyword provider is valid and has a valid suggest
// url.
bool valid_suggest_for_keyword_provider() const {
return keyword_provider_ && cached_keyword_provider_.suggestions_url();
}
// Returns true of the default provider is valid.
bool valid_default_provider() const { return !!default_provider_; }
// Returns true if the default provider is valid and has a valid suggest
// url.
bool valid_suggest_for_default_provider() const {
return default_provider_ && cached_default_provider_.suggestions_url();
}
// Returns true if |from_keyword_provider| is true, or
// the keyword provider is not valid.
bool is_primary_provider(bool from_keyword_provider) const {
return from_keyword_provider || !valid_keyword_provider();
}
private:
// Cached across the life of a query so we behave consistently even if the
// user changes their default while the query is running.
TemplateURL cached_default_provider_;
TemplateURL cached_keyword_provider_;
// TODO(pkasting): http://b/1162970 We shouldn't need these.
const TemplateURL* default_provider_;
const TemplateURL* keyword_provider_;
};
struct NavigationResult {
NavigationResult(const GURL& url, const string16& site_name)
: url(url),
site_name(site_name) {
}
// The URL.
GURL url;
// Name for the site.
string16 site_name;
};
typedef std::vector<string16> SuggestResults;
typedef std::vector<NavigationResult> NavigationResults;
typedef std::vector<history::KeywordSearchTermVisit> HistoryResults;
typedef std::map<string16, AutocompleteMatch> MatchMap;
// Called when timer_ expires.
void Run();
// Runs the history query, if necessary. The history query is synchronous.
// This does not update |done_|.
void DoHistoryQuery(bool minimal_changes);
// Determines whether an asynchronous subcomponent query should run for the
// current input. If so, starts it if necessary; otherwise stops it.
// NOTE: This function does not update |done_|. Callers must do so.
void StartOrStopSuggestQuery(bool minimal_changes);
// Returns true when the current query can be sent to the Suggest service.
// This will be false e.g. when Suggest is disabled, the query contains
// potentially private data, etc.
bool IsQuerySuitableForSuggest() const;
// Stops the suggest query.
// NOTE: This does not update |done_|. Callers must do so.
void StopSuggest();
// Creates a URLFetcher requesting suggest results for the specified
// TemplateURL. Ownership of the returned URLFetchet passes to the caller.
URLFetcher* CreateSuggestFetcher(int id,
const TemplateURL& provider,
const string16& text);
// Parses the results from the Suggest server and stores up to kMaxMatches of
// them in server_results_. Returns whether parsing succeeded.
bool ParseSuggestResults(Value* root_val,
bool is_keyword,
const string16& input_text,
SuggestResults* suggest_results);
// Converts the parsed server results in server_results_ to a set of
// AutocompleteMatches and adds them to |matches_|. This also sets |done_|
// correctly.
void ConvertResultsToAutocompleteMatches();
// Converts the first navigation result in |navigation_results| to an
// AutocompleteMatch and adds it to |matches_|.
void AddNavigationResultsToMatches(
const NavigationResults& navigation_results,
bool is_keyword);
// Adds a match for each result in |results| to |map|. |is_keyword| indicates
// whether the results correspond to the keyword provider or default provider.
void AddHistoryResultsToMap(const HistoryResults& results,
bool is_keyword,
int did_not_accept_suggestion,
MatchMap* map);
// Adds a match for each result in |suggest_results| to |map|. |is_keyword|
// indicates whether the results correspond to the keyword provider or default
// provider.
void AddSuggestResultsToMap(const SuggestResults& suggest_results,
bool is_keyword,
int did_not_accept_suggestion,
MatchMap* map);
// Determines the relevance for a particular match. We use different scoring
// algorithms for the different types of matches.
int CalculateRelevanceForWhatYouTyped() const;
// |time| is the time at which this query was last seen. |is_keyword| is true
// if the search is from the keyword provider. |looks_like_url| is true if the
// search term would be treated as a URL if typed into the omnibox.
int CalculateRelevanceForHistory(const base::Time& time,
bool looks_like_url,
bool is_keyword) const;
// |result_number| is the index of the suggestion in the result set from the
// server; the best suggestion is suggestion number 0. |is_keyword| is true
// if the search is from the keyword provider.
int CalculateRelevanceForSuggestion(size_t num_results,
size_t result_number,
bool is_keyword) const;
// |result_number| is same as above. |is_keyword| is true if the navigation
// result was suggested by the keyword provider.
int CalculateRelevanceForNavigation(size_t num_results,
size_t result_number,
bool is_keyword) const;
// Creates an AutocompleteMatch for "Search <engine> for |query_string|" with
// the supplied relevance. Adds this match to |map|; if such a match already
// exists, whichever one has lower relevance is eliminated.
void AddMatchToMap(const string16& query_string,
const string16& input_text,
int relevance,
AutocompleteMatch::Type type,
int accepted_suggestion,
bool is_keyword,
bool prevent_inline_autocomplete,
MatchMap* map);
// Returns an AutocompleteMatch for a navigational suggestion.
AutocompleteMatch NavigationToMatch(const NavigationResult& query_string,
int relevance,
bool is_keyword);
// Updates the value of |done_| from the internal state.
void UpdateDone();
// Updates the description/description_class of the first search match.
void UpdateFirstSearchMatchDescription();
// Should we query for suggest results immediately? This is normally false,
// but may be set to true during testing.
static bool query_suggest_immediately_;
// Maintains the TemplateURLs used.
Providers providers_;
// The user's input.
AutocompleteInput input_;
// Input text when searching against the keyword provider.
string16 keyword_input_text_;
// Searches in the user's history that begin with the input text.
HistoryResults keyword_history_results_;
HistoryResults default_history_results_;
// Number of suggest results that haven't yet arrived. If greater than 0 it
// indicates either |timer_| or one of the URLFetchers is still running.
int suggest_results_pending_;
// A timer to start a query to the suggest server after the user has stopped
// typing for long enough.
base::OneShotTimer<SearchProvider> timer_;
// The fetcher that retrieves suggest results for the keyword from the server.
scoped_ptr<URLFetcher> keyword_fetcher_;
// The fetcher that retrieves suggest results for the default engine from the
// server.
scoped_ptr<URLFetcher> default_fetcher_;
// Suggestions returned by the Suggest server for the input text.
SuggestResults keyword_suggest_results_;
SuggestResults default_suggest_results_;
// Navigational suggestions returned by the server.
NavigationResults keyword_navigation_results_;
NavigationResults default_navigation_results_;
// Whether suggest_results_ is valid.
bool have_suggest_results_;
// Has FinalizeInstantQuery been invoked since the last |Start|?
bool instant_finalized_;
// The |suggest_text| parameter passed to FinalizeInstantQuery.
string16 default_provider_suggest_text_;
DISALLOW_COPY_AND_ASSIGN(SearchProvider);
};
#endif // CHROME_BROWSER_AUTOCOMPLETE_SEARCH_PROVIDER_H_