blob: 025aed6d0d87377c7c8651be0eede7a1580b4c63 [file] [log] [blame]
// 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.
//
// 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 "base/time.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 "net/url_request/url_fetcher_delegate.h"
class Profile;
class TemplateURLService;
namespace base {
class Value;
}
namespace net {
class URLFetcher;
}
// 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 net::URLFetcherDelegate {
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) OVERRIDE;
virtual void Stop() OVERRIDE;
// Adds search-provider-specific information to omnibox event logs.
virtual void AddProviderInfo(ProvidersInfo* provider_info) const OVERRIDE;
// net::URLFetcherDelegate
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
// 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:
FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, SuggestRelevanceExperiment);
FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, NavigationInline);
FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, NavigationInlineSchemeSubstring);
FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, NavigationInlineDomainClassify);
virtual ~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:
explicit Providers(TemplateURLService* template_url_service);
// Returns true if the specified providers match the two providers cached
// by this class.
bool equal(const string16& default_provider,
const string16& keyword_provider) const {
return (default_provider == default_provider_) &&
(keyword_provider == keyword_provider_);
}
// Resets the cached providers.
void set(const string16& default_provider,
const string16& keyword_provider) {
default_provider_ = default_provider;
keyword_provider_ = keyword_provider;
}
TemplateURLService* template_url_service() { return template_url_service_; }
const string16& default_provider() const { return default_provider_; }
const string16& keyword_provider() const { return keyword_provider_; }
// NOTE: These may return NULL even if the provider members are nonempty!
const TemplateURL* GetDefaultProviderURL() const;
const TemplateURL* GetKeywordProviderURL() const;
// 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 || keyword_provider_.empty();
}
private:
TemplateURLService* template_url_service_;
// Cached across the life of a query so we behave consistently even if the
// user changes their default while the query is running.
string16 default_provider_;
string16 keyword_provider_;
DISALLOW_COPY_AND_ASSIGN(Providers);
};
// The Result classes are intermediate representations of AutocompleteMatches,
// simply containing relevance-ranked search and navigation suggestions.
// They may be cached to provide some synchronous matches while requests for
// new suggestions from updated input are in flight.
// TODO(msw) Extend these classes to generate their corresponding matches and
// other requisite data, in order to consolidate and simplify the
// highly fragmented SearchProvider logic for each Result type.
class Result {
public:
explicit Result(int relevance);
virtual ~Result();
int relevance() const { return relevance_; }
void set_relevance(int relevance) { relevance_ = relevance; }
private:
// The relevance score.
int relevance_;
};
class SuggestResult : public Result {
public:
SuggestResult(const string16& suggestion, int relevance);
virtual ~SuggestResult();
const string16& suggestion() const { return suggestion_; }
private:
// The search suggestion string.
string16 suggestion_;
};
class NavigationResult : public Result {
public:
NavigationResult(const GURL& url,
const string16& description,
int relevance);
virtual ~NavigationResult();
const GURL& url() const { return url_; }
const string16& description() const { return description_; }
private:
// The suggested url for navigation.
GURL url_;
// The suggested navigational result description; generally the site name.
string16 description_;
};
typedef std::vector<SuggestResult> SuggestResults;
typedef std::vector<NavigationResult> NavigationResults;
typedef std::vector<history::KeywordSearchTermVisit> HistoryResults;
typedef std::map<string16, AutocompleteMatch> MatchMap;
class CompareScoredResults;
// 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();
// Clears the current results.
void ClearResults();
// Remove results that cannot inline auto-complete the current input.
void RemoveStaleResults();
void RemoveStaleSuggestResults(SuggestResults* list, bool is_keyword);
void RemoveStaleNavigationResults(NavigationResults* list, bool is_keyword);
// Apply calculated relevance scores to the current results.
void ApplyCalculatedRelevance();
void ApplyCalculatedSuggestRelevance(SuggestResults* list, bool is_keyword);
void ApplyCalculatedNavigationRelevance(NavigationResults* list,
bool is_keyword);
// Creates a URLFetcher requesting suggest results from the specified
// |suggestions_url|. The caller owns the returned URLFetcher.
net::URLFetcher* CreateSuggestFetcher(
int id,
const TemplateURLRef& suggestions_url,
const string16& text);
// Parses results from the suggest server and updates the appropriate suggest
// and navigation result lists, depending on whether |is_keyword| is true.
// Returns whether the appropriate result list members were updated.
bool ParseSuggestResults(base::Value* root_val, bool is_keyword);
// Converts the parsed 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);
// Calculates relevance scores for all |results|.
SuggestResults ScoreHistoryResults(const HistoryResults& results,
bool base_prevent_inline_autocomplete,
bool input_multiple_words,
const string16& input_text,
bool is_keyword);
// Adds matches for |results| to |map|. |is_keyword| indicates whether the
// results correspond to the keyword provider or default provider.
void AddSuggestResultsToMap(const SuggestResults& results,
bool is_keyword,
MatchMap* map);
// Get the relevance score for the verbatim result; this value may be provided
// by the suggest server; otherwise it is calculated locally.
int GetVerbatimRelevance() const;
// Calculate the relevance score for the verbatim result.
int CalculateRelevanceForVerbatim() const;
// |time| is the time at which this query was last seen. |is_keyword|
// indicates whether the results correspond to the keyword provider or default
// provider. |prevent_inline_autocomplete| is true if we should not inline
// autocomplete this query.
int CalculateRelevanceForHistory(const base::Time& time,
bool is_keyword,
bool prevent_inline_autocomplete) const;
// Calculate the relevance for search suggestion results. Set |for_keyword| to
// true for relevance values applicable to keyword provider results.
int CalculateRelevanceForSuggestion(bool for_keyword) const;
// Calculate the relevance for navigation results. Set |for_keyword| to true
// for relevance values applicable to keyword provider results.
int CalculateRelevanceForNavigation(bool for_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,
MatchMap* map);
// Returns an AutocompleteMatch for a navigational suggestion.
AutocompleteMatch NavigationToMatch(const NavigationResult& navigation,
bool is_keyword);
// Updates the value of |done_| from the internal state.
void UpdateDone();
// 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 time at which we sent a query to the suggest server.
base::TimeTicks time_suggest_request_sent_;
// Fetchers used to retrieve results for the keyword and default providers.
scoped_ptr<net::URLFetcher> keyword_fetcher_;
scoped_ptr<net::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_;
// A flag indicating use of server supplied relevance scores.
bool has_suggested_relevance_;
// The server supplied verbatim relevance score. Negative values indicate that
// there is no suggested score; a value of 0 suppresses the verbatim result.
int verbatim_relevance_;
// 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_