This source file includes following definitions.
- AutocompleteMatchToAssistedQuery
- AppendAvailableAutocompletion
- IsTrivialAutocompletion
- AutocompleteMatchHasCustomDescription
- profile_
- Start
- Stop
- StartZeroSuggest
- DeleteMatch
- ExpireCopiedEntries
- OnProviderUpdate
- AddProvidersInfo
- ResetSession
- UpdateMatchDestinationURL
- UpdateResult
- UpdateAssociatedKeywords
- UpdateKeywordDescriptions
- UpdateAssistedQueryStats
- NotifyChanged
- CheckIfDone
- StartExpireTimer
- StartStopTimer
#include "chrome/browser/autocomplete/autocomplete_controller.h"
#include <set>
#include <string>
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/autocomplete/autocomplete_controller_delegate.h"
#include "chrome/browser/autocomplete/bookmark_provider.h"
#include "chrome/browser/autocomplete/builtin_provider.h"
#include "chrome/browser/autocomplete/extension_app_provider.h"
#include "chrome/browser/autocomplete/history_quick_provider.h"
#include "chrome/browser/autocomplete/history_url_provider.h"
#include "chrome/browser/autocomplete/keyword_provider.h"
#include "chrome/browser/autocomplete/search_provider.h"
#include "chrome/browser/autocomplete/shortcuts_provider.h"
#include "chrome/browser/autocomplete/zero_suggest_provider.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/omnibox/omnibox_field_trial.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url.h"
#include "content/public/browser/notification_service.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
void AutocompleteMatchToAssistedQuery(
const AutocompleteMatch::Type& match, size_t* type, size_t* subtype) {
*type = 69;
*subtype = base::string16::npos;
switch (match) {
case AutocompleteMatchType::SEARCH_SUGGEST: {
*type = 0;
return;
}
case AutocompleteMatchType::SEARCH_SUGGEST_ENTITY: {
*subtype = 46;
return;
}
case AutocompleteMatchType::SEARCH_SUGGEST_INFINITE: {
*subtype = 33;
return;
}
case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED: {
*subtype = 35;
return;
}
case AutocompleteMatchType::SEARCH_SUGGEST_PROFILE: {
*subtype = 44;
return;
}
case AutocompleteMatchType::NAVSUGGEST: {
*type = 5;
return;
}
case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED: {
*subtype = 57;
return;
}
case AutocompleteMatchType::URL_WHAT_YOU_TYPED: {
*subtype = 58;
return;
}
case AutocompleteMatchType::SEARCH_HISTORY: {
*subtype = 59;
return;
}
case AutocompleteMatchType::HISTORY_URL: {
*subtype = 60;
return;
}
case AutocompleteMatchType::HISTORY_TITLE: {
*subtype = 61;
return;
}
case AutocompleteMatchType::HISTORY_BODY: {
*subtype = 62;
return;
}
case AutocompleteMatchType::HISTORY_KEYWORD: {
*subtype = 63;
return;
}
case AutocompleteMatchType::BOOKMARK_TITLE: {
*subtype = 65;
return;
}
default: {
*subtype = 64;
}
}
}
void AppendAvailableAutocompletion(size_t type,
size_t subtype,
int count,
std::string* autocompletions) {
if (!autocompletions->empty())
autocompletions->append("j");
base::StringAppendF(autocompletions, "%" PRIuS, type);
if (subtype != base::string16::npos)
base::StringAppendF(autocompletions, "i%" PRIuS, subtype);
if (count > 1)
base::StringAppendF(autocompletions, "l%d", count);
}
bool IsTrivialAutocompletion(const AutocompleteMatch& match) {
return match.type == AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED ||
match.type == AutocompleteMatchType::URL_WHAT_YOU_TYPED ||
match.type == AutocompleteMatchType::SEARCH_OTHER_ENGINE;
}
bool AutocompleteMatchHasCustomDescription(const AutocompleteMatch& match) {
return match.type == AutocompleteMatchType::SEARCH_SUGGEST_ENTITY ||
match.type == AutocompleteMatchType::SEARCH_SUGGEST_PROFILE;
}
}
AutocompleteController::AutocompleteController(
Profile* profile,
AutocompleteControllerDelegate* delegate,
int provider_types)
: delegate_(delegate),
history_url_provider_(NULL),
keyword_provider_(NULL),
search_provider_(NULL),
zero_suggest_provider_(NULL),
stop_timer_duration_(OmniboxFieldTrial::StopTimerFieldTrialDuration()),
done_(true),
in_start_(false),
profile_(profile) {
provider_types &= ~OmniboxFieldTrial::GetDisabledProviderTypes();
if (provider_types & AutocompleteProvider::TYPE_BOOKMARK)
providers_.push_back(new BookmarkProvider(this, profile));
if (provider_types & AutocompleteProvider::TYPE_BUILTIN)
providers_.push_back(new BuiltinProvider(this, profile));
if (provider_types & AutocompleteProvider::TYPE_EXTENSION_APP)
providers_.push_back(new ExtensionAppProvider(this, profile));
if (provider_types & AutocompleteProvider::TYPE_HISTORY_QUICK)
providers_.push_back(new HistoryQuickProvider(this, profile));
if (provider_types & AutocompleteProvider::TYPE_HISTORY_URL) {
history_url_provider_ = new HistoryURLProvider(this, profile);
providers_.push_back(history_url_provider_);
}
#if !defined(OS_ANDROID)
if (provider_types & AutocompleteProvider::TYPE_KEYWORD) {
keyword_provider_ = new KeywordProvider(this, profile);
providers_.push_back(keyword_provider_);
}
#endif
if (provider_types & AutocompleteProvider::TYPE_SEARCH) {
search_provider_ = new SearchProvider(this, profile);
providers_.push_back(search_provider_);
}
if (provider_types & AutocompleteProvider::TYPE_SHORTCUTS)
providers_.push_back(new ShortcutsProvider(this, profile));
if (provider_types & AutocompleteProvider::TYPE_ZERO_SUGGEST) {
zero_suggest_provider_ = ZeroSuggestProvider::Create(this, profile);
if (zero_suggest_provider_)
providers_.push_back(zero_suggest_provider_);
}
for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i)
(*i)->AddRef();
}
AutocompleteController::~AutocompleteController() {
result_.Reset();
Stop(false);
for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i)
(*i)->Release();
providers_.clear();
}
void AutocompleteController::Start(const AutocompleteInput& input) {
const base::string16 old_input_text(input_.text());
const AutocompleteInput::MatchesRequested old_matches_requested =
input_.matches_requested();
input_ = input;
const bool minimal_changes = (input_.text() == old_input_text) &&
(input_.matches_requested() == old_matches_requested);
expire_timer_.Stop();
stop_timer_.Stop();
in_start_ = true;
base::TimeTicks start_time = base::TimeTicks::Now();
for (ACProviders::iterator i(providers_.begin()); i != providers_.end();
++i) {
base::TimeTicks provider_start_time = base::TimeTicks::Now();
if (*i == zero_suggest_provider_)
(*i)->Start(AutocompleteInput(), minimal_changes);
else
(*i)->Start(input_, minimal_changes);
if (input.matches_requested() != AutocompleteInput::ALL_MATCHES)
DCHECK((*i)->done());
base::TimeTicks provider_end_time = base::TimeTicks::Now();
std::string name = std::string("Omnibox.ProviderTime.") + (*i)->GetName();
base::HistogramBase* counter = base::Histogram::FactoryGet(
name, 1, 5000, 20, base::Histogram::kUmaTargetedHistogramFlag);
counter->Add(static_cast<int>(
(provider_end_time - provider_start_time).InMilliseconds()));
}
if (input.matches_requested() == AutocompleteInput::ALL_MATCHES &&
(input.text().length() < 6)) {
base::TimeTicks end_time = base::TimeTicks::Now();
std::string name = "Omnibox.QueryTime." + base::IntToString(
input.text().length());
base::HistogramBase* counter = base::Histogram::FactoryGet(
name, 1, 1000, 50, base::Histogram::kUmaTargetedHistogramFlag);
counter->Add(static_cast<int>((end_time - start_time).InMilliseconds()));
}
in_start_ = false;
CheckIfDone();
UpdateResult(false, true);
if (!done_) {
StartExpireTimer();
StartStopTimer();
}
}
void AutocompleteController::Stop(bool clear_result) {
for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end();
++i) {
(*i)->Stop(clear_result);
}
expire_timer_.Stop();
stop_timer_.Stop();
done_ = true;
if (clear_result && !result_.empty()) {
result_.Reset();
NotifyChanged(false);
}
}
void AutocompleteController::StartZeroSuggest(const AutocompleteInput& input) {
if (zero_suggest_provider_ != NULL) {
DCHECK(!in_start_);
for (ACProviders::iterator i(providers_.begin()); i != providers_.end();
++i) {
if (*i == zero_suggest_provider_)
(*i)->Start(input, false);
else
(*i)->Start(AutocompleteInput(), false);
}
}
}
void AutocompleteController::DeleteMatch(const AutocompleteMatch& match) {
DCHECK(match.SupportsDeletion());
for (ACMatches::const_iterator it(match.duplicate_matches.begin());
it != match.duplicate_matches.end(); ++it) {
if (it->deletable)
it->provider->DeleteMatch(*it);
}
if (match.deletable)
match.provider->DeleteMatch(match);
OnProviderUpdate(true);
ExpireCopiedEntries();
}
void AutocompleteController::ExpireCopiedEntries() {
UpdateResult(true, false);
}
void AutocompleteController::OnProviderUpdate(bool updated_matches) {
CheckIfDone();
if (!in_start_ && (updated_matches || done_))
UpdateResult(false, false);
}
void AutocompleteController::AddProvidersInfo(
ProvidersInfo* provider_info) const {
provider_info->clear();
for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end();
++i) {
(*i)->AddProviderInfo(provider_info);
}
}
void AutocompleteController::ResetSession() {
for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end();
++i)
(*i)->ResetSession();
}
void AutocompleteController::UpdateMatchDestinationURL(
base::TimeDelta query_formulation_time,
AutocompleteMatch* match) const {
TemplateURL* template_url = match->GetTemplateURL(profile_, false);
if (!template_url || !match->search_terms_args.get() ||
match->search_terms_args->assisted_query_stats.empty())
return;
TemplateURLRef::SearchTermsArgs search_terms_args(*match->search_terms_args);
search_terms_args.assisted_query_stats += base::StringPrintf(
".%" PRId64 "j%dj%d",
query_formulation_time.InMilliseconds(),
(search_provider_ &&
search_provider_->field_trial_triggered_in_session()) ||
(zero_suggest_provider_ &&
zero_suggest_provider_->field_trial_triggered_in_session()),
input_.current_page_classification());
match->destination_url =
GURL(template_url->url_ref().ReplaceSearchTerms(search_terms_args));
}
void AutocompleteController::UpdateResult(
bool regenerate_result,
bool force_notify_default_match_changed) {
const bool last_default_was_valid = result_.default_match() != result_.end();
base::string16 last_default_fill_into_edit, last_default_keyword,
last_default_associated_keyword;
if (last_default_was_valid) {
last_default_fill_into_edit = result_.default_match()->fill_into_edit;
last_default_keyword = result_.default_match()->keyword;
if (result_.default_match()->associated_keyword != NULL)
last_default_associated_keyword =
result_.default_match()->associated_keyword->keyword;
}
if (regenerate_result)
result_.Reset();
AutocompleteResult last_result;
last_result.Swap(&result_);
for (ACProviders::const_iterator i(providers_.begin());
i != providers_.end(); ++i)
result_.AppendMatches((*i)->matches());
result_.SortAndCull(input_, profile_);
#ifndef NDEBUG
result_.Validate();
#endif
if (!done_) {
result_.CopyOldMatches(input_, last_result, profile_);
}
UpdateKeywordDescriptions(&result_);
UpdateAssociatedKeywords(&result_);
UpdateAssistedQueryStats(&result_);
const bool default_is_valid = result_.default_match() != result_.end();
base::string16 default_associated_keyword;
if (default_is_valid &&
(result_.default_match()->associated_keyword != NULL)) {
default_associated_keyword =
result_.default_match()->associated_keyword->keyword;
}
const bool notify_default_match =
(last_default_was_valid != default_is_valid) ||
(last_default_was_valid &&
((result_.default_match()->fill_into_edit !=
last_default_fill_into_edit) ||
(default_associated_keyword != last_default_associated_keyword) ||
(result_.default_match()->keyword != last_default_keyword)));
if (notify_default_match)
last_time_default_match_changed_ = base::TimeTicks::Now();
NotifyChanged(force_notify_default_match_changed || notify_default_match);
}
void AutocompleteController::UpdateAssociatedKeywords(
AutocompleteResult* result) {
if (!keyword_provider_)
return;
std::set<base::string16> keywords;
for (ACMatches::iterator match(result->begin()); match != result->end();
++match) {
base::string16 keyword(
match->GetSubstitutingExplicitlyInvokedKeyword(profile_));
if (!keyword.empty()) {
keywords.insert(keyword);
continue;
}
keyword = match->associated_keyword.get() ?
match->associated_keyword->keyword :
keyword_provider_->GetKeywordForText(match->fill_into_edit);
if (!keyword.empty() && !keywords.count(keyword)) {
keywords.insert(keyword);
if (!match->associated_keyword.get())
match->associated_keyword.reset(new AutocompleteMatch(
keyword_provider_->CreateVerbatimMatch(match->fill_into_edit,
keyword, input_)));
} else {
match->associated_keyword.reset();
}
}
}
void AutocompleteController::UpdateKeywordDescriptions(
AutocompleteResult* result) {
base::string16 last_keyword;
for (AutocompleteResult::iterator i(result->begin()); i != result->end();
++i) {
if (AutocompleteMatch::IsSearchType(i->type)) {
if (AutocompleteMatchHasCustomDescription(*i))
continue;
i->description.clear();
i->description_class.clear();
DCHECK(!i->keyword.empty());
if (i->keyword != last_keyword) {
const TemplateURL* template_url = i->GetTemplateURL(profile_, false);
if (template_url) {
i->description = template_url->AdjustedShortNameForLocaleDirection();
if (template_url->GetType() != TemplateURL::OMNIBOX_API_EXTENSION) {
i->description = l10n_util::GetStringFUTF16(
IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION, i->description);
}
i->description_class.push_back(
ACMatchClassification(0, ACMatchClassification::DIM));
}
last_keyword = i->keyword;
}
} else {
last_keyword.clear();
}
}
}
void AutocompleteController::UpdateAssistedQueryStats(
AutocompleteResult* result) {
if (result->empty())
return;
std::string autocompletions;
int count = 0;
size_t last_type = base::string16::npos;
size_t last_subtype = base::string16::npos;
for (ACMatches::iterator match(result->begin()); match != result->end();
++match) {
size_t type = base::string16::npos;
size_t subtype = base::string16::npos;
AutocompleteMatchToAssistedQuery(match->type, &type, &subtype);
if (last_type != base::string16::npos &&
(type != last_type || subtype != last_subtype)) {
AppendAvailableAutocompletion(
last_type, last_subtype, count, &autocompletions);
count = 1;
} else {
count++;
}
last_type = type;
last_subtype = subtype;
}
AppendAvailableAutocompletion(
last_type, last_subtype, count, &autocompletions);
for (size_t index = 0; index < result->size(); ++index) {
AutocompleteMatch* match = result->match_at(index);
const TemplateURL* template_url = match->GetTemplateURL(profile_, false);
if (!template_url || !match->search_terms_args.get())
continue;
std::string selected_index;
if (!IsTrivialAutocompletion(*match))
selected_index = base::StringPrintf("%" PRIuS, index);
match->search_terms_args->assisted_query_stats =
base::StringPrintf("chrome.%s.%s",
selected_index.c_str(),
autocompletions.c_str());
match->destination_url = GURL(template_url->url_ref().ReplaceSearchTerms(
*match->search_terms_args));
}
}
void AutocompleteController::NotifyChanged(bool notify_default_match) {
if (delegate_)
delegate_->OnResultChanged(notify_default_match);
if (done_) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY,
content::Source<AutocompleteController>(this),
content::NotificationService::NoDetails());
}
}
void AutocompleteController::CheckIfDone() {
for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end();
++i) {
if (!(*i)->done()) {
done_ = false;
return;
}
}
done_ = true;
}
void AutocompleteController::StartExpireTimer() {
const int kExpireTimeMS = 500;
if (result_.HasCopiedMatches())
expire_timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kExpireTimeMS),
this, &AutocompleteController::ExpireCopiedEntries);
}
void AutocompleteController::StartStopTimer() {
stop_timer_.Start(FROM_HERE,
stop_timer_duration_,
base::Bind(&AutocompleteController::Stop,
base::Unretained(this),
false));
}