Commit 558552c0 authored by hashimoto's avatar hashimoto Committed by Commit bot

Use SearchProvider for Athena

With SearchProvider, we can suggest URLs as well as queries and show icons at the left of each suggested item.
Also, parts of the suggested query which is not typed by the user are shown bold.

Basically do the same thing as Chrome's AutocompleteController+OmniboxProvider.
Add required resources to athena_resources.

BUG=408050

Review URL: https://codereview.chromium.org/514583002

Cr-Commit-Position: refs/heads/master@{#292350}
parent 1914f8ea
......@@ -11,17 +11,15 @@
#include "components/metrics/proto/omnibox_event.pb.h"
#include "components/metrics/proto/omnibox_input_type.pb.h"
#include "components/omnibox/autocomplete_input.h"
#include "components/omnibox/autocomplete_provider_client.h"
#include "components/omnibox/autocomplete_scheme_classifier.h"
#include "components/omnibox/search_suggestion_parser.h"
#include "components/omnibox/search_provider.h"
#include "components/search_engines/search_terms_data.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_engines/template_url_service_client.h"
#include "content/public/browser/browser_context.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request.h"
#include "ui/app_list/search_result.h"
#include "ui/base/resource/resource_bundle.h"
#include "url/gurl.h"
namespace athena {
......@@ -75,66 +73,147 @@ class AthenaTemplateURLServiceClient : public TemplateURLServiceClient {
DISALLOW_COPY_AND_ASSIGN(AthenaTemplateURLServiceClient);
};
// The AutocompleteProviderClient for Athena.
class AthenaAutocompleteProviderClient : public AutocompleteProviderClient {
public:
explicit AthenaAutocompleteProviderClient(
content::BrowserContext* browser_context)
: browser_context_(browser_context) {}
virtual ~AthenaAutocompleteProviderClient() {}
virtual net::URLRequestContextGetter* RequestContext() OVERRIDE {
return browser_context_->GetRequestContext();
}
virtual bool IsOffTheRecord() OVERRIDE {
return browser_context_->IsOffTheRecord();
}
virtual std::string AcceptLanguages() OVERRIDE {
// TODO(hashimoto): Return the value stored in the prefs.
return "en-US";
}
virtual bool SearchSuggestEnabled() OVERRIDE { return true; }
virtual bool ShowBookmarkBar() OVERRIDE { return false; }
virtual const AutocompleteSchemeClassifier& SchemeClassifier() OVERRIDE {
return scheme_classifier_;
}
virtual void Classify(
const base::string16& text,
bool prefer_keyword,
bool allow_exact_keyword_match,
metrics::OmniboxEventProto::PageClassification page_classification,
AutocompleteMatch* match,
GURL* alternate_nav_url) OVERRIDE {}
virtual history::URLDatabase* InMemoryDatabase() OVERRIDE { return NULL; }
virtual void DeleteMatchingURLsForKeywordFromHistory(
history::KeywordID keyword_id,
const base::string16& term) OVERRIDE {}
virtual bool TabSyncEnabledAndUnencrypted() OVERRIDE { return false; }
virtual void PrefetchImage(const GURL& url) OVERRIDE {}
private:
content::BrowserContext* browser_context_;
AthenaSchemeClassifier scheme_classifier_;
DISALLOW_COPY_AND_ASSIGN(AthenaAutocompleteProviderClient);
};
int ACMatchStyleToTagStyle(int styles) {
int tag_styles = 0;
if (styles & ACMatchClassification::URL)
tag_styles |= app_list::SearchResult::Tag::URL;
if (styles & ACMatchClassification::MATCH)
tag_styles |= app_list::SearchResult::Tag::MATCH;
if (styles & ACMatchClassification::DIM)
tag_styles |= app_list::SearchResult::Tag::DIM;
return tag_styles;
}
// Translates ACMatchClassifications into SearchResult tags.
void ACMatchClassificationsToTags(
const base::string16& text,
const ACMatchClassifications& text_classes,
app_list::SearchResult::Tags* tags) {
int tag_styles = app_list::SearchResult::Tag::NONE;
size_t tag_start = 0;
for (size_t i = 0; i < text_classes.size(); ++i) {
const ACMatchClassification& text_class = text_classes[i];
// Closes current tag.
if (tag_styles != app_list::SearchResult::Tag::NONE) {
tags->push_back(app_list::SearchResult::Tag(
tag_styles, tag_start, text_class.offset));
tag_styles = app_list::SearchResult::Tag::NONE;
}
if (text_class.style == ACMatchClassification::NONE)
continue;
tag_start = text_class.offset;
tag_styles = ACMatchStyleToTagStyle(text_class.style);
}
if (tag_styles != app_list::SearchResult::Tag::NONE) {
tags->push_back(app_list::SearchResult::Tag(
tag_styles, tag_start, text.length()));
}
}
class UrlSearchResult : public app_list::SearchResult {
public:
UrlSearchResult(content::BrowserContext* browser_context,
const GURL& url,
const base::string16& title)
const AutocompleteMatch& match)
: browser_context_(browser_context),
url_(url) {
set_title(title);
set_id(url_.spec());
match_(match) {
set_id(match_.destination_url.spec());
// Derive relevance from omnibox relevance and normalize it to [0, 1].
// The magic number 1500 is the highest score of an omnibox result.
// See comments in autocomplete_provider.h.
set_relevance(match_.relevance / 1500.0);
UpdateIcon();
UpdateTitleAndDetails();
}
virtual ~UrlSearchResult() {}
private:
// Overriddenn from app_list::SearchResult:
virtual void Open(int event_flags) OVERRIDE {
ActivityManager::Get()->AddActivity(
ActivityFactory::Get()->CreateWebActivity(browser_context_, url_));
ActivityFactory::Get()->CreateWebActivity(browser_context_,
match_.destination_url));
}
void UpdateIcon() {
SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
AutocompleteMatch::TypeToIcon(match_.type)));
}
void UpdateTitleAndDetails() {
set_title(match_.contents);
SearchResult::Tags title_tags;
ACMatchClassificationsToTags(match_.contents,
match_.contents_class,
&title_tags);
set_title_tags(title_tags);
set_details(match_.description);
SearchResult::Tags details_tags;
ACMatchClassificationsToTags(match_.description,
match_.description_class,
&details_tags);
set_details_tags(details_tags);
}
content::BrowserContext* browser_context_;
GURL url_;
AutocompleteMatch match_;
DISALLOW_COPY_AND_ASSIGN(UrlSearchResult);
};
scoped_ptr<app_list::SearchResult> CreateResultForSearchQuery(
content::BrowserContext* browser_context,
TemplateURLService* template_url_service,
const base::string16& search_query) {
TemplateURL* template_url =
template_url_service->GetDefaultSearchProvider();
const TemplateURLRef& search_url = template_url->url_ref();
TemplateURLRef::SearchTermsArgs search_terms_args(search_query);
return scoped_ptr<app_list::SearchResult>(new UrlSearchResult(
browser_context,
GURL(search_url.ReplaceSearchTerms(
search_terms_args, template_url_service->search_terms_data())),
search_query));
}
scoped_ptr<app_list::SearchResult> CreateResultForInput(
content::BrowserContext* browser_context,
TemplateURLService* template_url_service,
const AutocompleteInput& input) {
scoped_ptr<app_list::SearchResult> result;
app_list::SearchResult::Tags title_tags;
if (input.type() == metrics::OmniboxInputType::URL) {
result.reset(new UrlSearchResult(
browser_context, input.canonicalized_url(), input.text()));
title_tags.push_back(app_list::SearchResult::Tag(
app_list::SearchResult::Tag::URL, 0, input.text().size()));
} else {
result = CreateResultForSearchQuery(
browser_context, template_url_service, input.text());
}
title_tags.push_back(app_list::SearchResult::Tag(
app_list::SearchResult::Tag::MATCH, 0, input.text().size()));
result->set_title_tags(title_tags);
return result.Pass();
}
} // namespace
UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context)
......@@ -150,7 +229,11 @@ UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context)
NULL /*GoogleURLTracker */,
NULL /* RapporService */,
base::Closure() /* dsp_change_callback */)),
should_fetch_suggestions_again_(false) {
provider_(new ::SearchProvider(
this,
template_url_service_.get(),
scoped_ptr<AutocompleteProviderClient>(
new AthenaAutocompleteProviderClient(browser_context_)))) {
template_url_service_->Load();
}
......@@ -158,6 +241,7 @@ UrlSearchProvider::~UrlSearchProvider() {
}
void UrlSearchProvider::Start(const base::string16& query) {
const bool minimal_changes = query == input_.text();
input_ = AutocompleteInput(query,
base::string16::npos /* cursor_position */,
base::string16() /* desired_tld */,
......@@ -168,80 +252,29 @@ void UrlSearchProvider::Start(const base::string16& query) {
true /* allow_extract_keyword_match */,
true /* want_asynchronous_matches */,
AthenaSchemeClassifier());
ClearResults();
Add(CreateResultForInput(browser_context_, template_url_service_.get(),
input_));
StartFetchingSuggestions();
}
void UrlSearchProvider::Stop() {
suggestion_fetcher_.reset();
provider_->Start(input_, minimal_changes);
}
void UrlSearchProvider::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK_EQ(suggestion_fetcher_.get(), source);
if (source->GetStatus().is_success() && source->GetResponseCode() == 200) {
std::string json_data = SearchSuggestionParser::ExtractJsonData(source);
scoped_ptr<base::Value> data(
SearchSuggestionParser::DeserializeJsonData(json_data));
if (data) {
const int kDefaultRelevance = 0;
SearchSuggestionParser::Results results;
if (SearchSuggestionParser::ParseSuggestResults(
*data, input_, AthenaSchemeClassifier(), kDefaultRelevance,
std::string(), // languages
false, // is_keyword_result
&results)) {
ClearResults();
Add(CreateResultForInput(browser_context_, template_url_service_.get(),
input_));
for (size_t i = 0; i < results.suggest_results.size(); ++i) {
const SearchSuggestionParser::SuggestResult& result =
results.suggest_results[i];
Add(CreateResultForSearchQuery(browser_context_,
template_url_service_.get(),
result.suggestion()));
}
for (size_t i = 0; i < results.navigation_results.size(); ++i) {
const SearchSuggestionParser::NavigationResult& result =
results.navigation_results[i];
Add(scoped_ptr<app_list::SearchResult>(
new UrlSearchResult(browser_context_, result.url(),
result.description())));
}
}
}
}
suggestion_fetcher_.reset();
if (should_fetch_suggestions_again_)
StartFetchingSuggestions();
void UrlSearchProvider::Stop() {
provider_->Stop(false);
}
void UrlSearchProvider::StartFetchingSuggestions() {
if (suggestion_fetcher_) {
should_fetch_suggestions_again_ = true;
return;
}
should_fetch_suggestions_again_ = false;
// Bail if the suggestion URL is invalid with the given replacements.
TemplateURL* template_url = template_url_service_->GetDefaultSearchProvider();
TemplateURLRef::SearchTermsArgs search_term_args(input_.text());
search_term_args.input_type = input_.type();
search_term_args.cursor_position = input_.cursor_position();
search_term_args.page_classification = input_.current_page_classification();
GURL suggest_url(template_url->suggestions_url_ref().ReplaceSearchTerms(
search_term_args, template_url_service_->search_terms_data()));
if (!suggest_url.is_valid()) {
DLOG(ERROR) << "Invalid URL: " << suggest_url;
void UrlSearchProvider::OnProviderUpdate(bool updated_matches) {
if (!updated_matches)
return;
ClearResults();
const ACMatches& matches = provider_->matches();
for (ACMatches::const_iterator it = matches.begin(); it != matches.end();
++it) {
if (!it->destination_url.is_valid())
continue;
Add(scoped_ptr<app_list::SearchResult>(new UrlSearchResult(
browser_context_, *it)));
}
suggestion_fetcher_.reset(
net::URLFetcher::Create(suggest_url, net::URLFetcher::GET, this));
suggestion_fetcher_->SetRequestContext(browser_context_->GetRequestContext());
suggestion_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES);
suggestion_fetcher_->Start();
}
} // namespace athena
......@@ -5,11 +5,13 @@
#ifndef ATHENA_MAIN_URL_SEARCH_PROVIDER_H_
#define ATHENA_MAIN_URL_SEARCH_PROVIDER_H_
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "components/omnibox/autocomplete_input.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "components/omnibox/autocomplete_provider_listener.h"
#include "ui/app_list/search_provider.h"
class AutocompleteProvider;
class TemplateURLService;
namespace content {
......@@ -20,7 +22,7 @@ namespace athena {
// A sample search provider.
class UrlSearchProvider : public app_list::SearchProvider,
public net::URLFetcherDelegate {
public AutocompleteProviderListener {
public:
UrlSearchProvider(content::BrowserContext* browser_context);
virtual ~UrlSearchProvider();
......@@ -29,20 +31,17 @@ class UrlSearchProvider : public app_list::SearchProvider,
virtual void Start(const base::string16& query) OVERRIDE;
virtual void Stop() OVERRIDE;
// Overridden from net::URLFetcherDelegate.
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
// Overridden from AutocompleteProviderListener
virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
private:
void StartFetchingSuggestions();
content::BrowserContext* browser_context_;
// TODO(mukai): This should be provided through BrowserContextKeyedService.
scoped_ptr<TemplateURLService> template_url_service_;
AutocompleteInput input_;
scoped_ptr<net::URLFetcher> suggestion_fetcher_;
bool should_fetch_suggestions_again_;
scoped_refptr<AutocompleteProvider> provider_;
DISALLOW_COPY_AND_ASSIGN(UrlSearchProvider);
};
......
......@@ -29,6 +29,8 @@
'type': 'none',
'dependencies': [
'../../ash/ash_resources.gyp:ash_resources',
'../../components/components_resources.gyp:components_resources',
'../../components/components_strings.gyp:components_strings',
'../../content/app/resources/content_resources.gyp:content_resources',
'../../content/app/strings/content_strings.gyp:content_strings',
'../../extensions/extensions.gyp:extensions_shell_and_test_pak',
......@@ -43,6 +45,9 @@
'<(PRODUCT_DIR)/extensions_shell_and_test.pak',
'<(SHARED_INTERMEDIATE_DIR)/ash/resources/ash_resources_100_percent.pak',
'<(SHARED_INTERMEDIATE_DIR)/athena/resources/athena_resources_100_percent.pak',
'<(SHARED_INTERMEDIATE_DIR)/components/component_resources_100_percent.pak',
'<(SHARED_INTERMEDIATE_DIR)/components/component_resources.pak',
'<(SHARED_INTERMEDIATE_DIR)/components/strings/components_strings_en-US.pak',
'<(SHARED_INTERMEDIATE_DIR)/content/app/resources/content_resources_100_percent.pak',
'<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.pak',
'<(SHARED_INTERMEDIATE_DIR)/ui/chromeos/resources/ui_chromeos_resources_100_percent.pak',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment