blob: 6ab9498054899a08c5bdfec5eb677a03f463faa7 [file] [log] [blame]
// Copyright 2014 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.
#include "athena/main/url_search_provider.h"
#include "athena/activity/public/activity_factory.h"
#include "athena/activity/public/activity_manager.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#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_scheme_classifier.h"
#include "components/omnibox/search_suggestion_parser.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 "url/gurl.h"
namespace athena {
namespace {
// The SearchTermsData implementation for Athena.
class AthenaSearchTermsData : public SearchTermsData {
public:
// SearchTermsData:
virtual std::string GetSuggestClient() const OVERRIDE {
return "chrome";
}
};
// The AutocompleteSchemeClassifier implementation for Athena.
// TODO(mukai): Introduce supports of normal schemes like about: or blob:
class AthenaSchemeClassifier : public AutocompleteSchemeClassifier {
public:
AthenaSchemeClassifier() {}
// AutocompleteSchemeClassifier:
virtual metrics::OmniboxInputType::Type GetInputTypeForScheme(
const std::string& scheme) const OVERRIDE {
if (net::URLRequest::IsHandledProtocol(scheme))
return metrics::OmniboxInputType::URL;
return metrics::OmniboxInputType::INVALID;
}
};
// The templateURLServiceClient for Athena. Mainly for the interaction with
// history module (see chrome/browser/search_engines for Chrome implementation).
// TODO(mukai): Implement the contents of this class when it's necessary.
class AthenaTemplateURLServiceClient : public TemplateURLServiceClient {
public:
AthenaTemplateURLServiceClient() {}
virtual ~AthenaTemplateURLServiceClient() {}
private:
// TemplateURLServiceClient:
virtual void SetOwner(TemplateURLService* owner) OVERRIDE {}
virtual void DeleteAllSearchTermsForKeyword(TemplateURLID id) OVERRIDE {}
virtual void SetKeywordSearchTermsForURL(
const GURL& url,
TemplateURLID id,
const base::string16& term) OVERRIDE {}
virtual void AddKeywordGeneratedVisit(const GURL& url) OVERRIDE {}
virtual void RestoreExtensionInfoIfNecessary(
TemplateURL* template_url) OVERRIDE {}
DISALLOW_COPY_AND_ASSIGN(AthenaTemplateURLServiceClient);
};
class UrlSearchResult : public app_list::SearchResult {
public:
UrlSearchResult(content::BrowserContext* browser_context,
const GURL& url,
const base::string16& title)
: browser_context_(browser_context),
url_(url) {
set_title(title);
set_id(url_.spec());
}
private:
// Overriddenn from app_list::SearchResult:
virtual void Open(int event_flags) OVERRIDE {
ActivityManager::Get()->AddActivity(
ActivityFactory::Get()->CreateWebActivity(browser_context_, url_));
}
content::BrowserContext* browser_context_;
GURL url_;
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)
: browser_context_(browser_context),
// TODO(mukai): introduce the real parameters when it's necessary.
template_url_service_(
new TemplateURLService(NULL /* prefs */,
scoped_ptr<SearchTermsData>(
new AthenaSearchTermsData()),
NULL /* KeywordWebDataService */,
scoped_ptr<TemplateURLServiceClient>(
new AthenaTemplateURLServiceClient()),
NULL /*GoogleURLTracker */,
NULL /* RapporService */,
base::Closure() /* dsp_change_callback */)),
should_fetch_suggestions_again_(false) {
template_url_service_->Load();
}
UrlSearchProvider::~UrlSearchProvider() {
}
void UrlSearchProvider::Start(const base::string16& query) {
input_ = AutocompleteInput(query,
base::string16::npos /* cursor_position */,
base::string16() /* desired_tld */,
GURL() /* current_url */,
metrics::OmniboxEventProto::INVALID_SPEC,
false /* prevent_inline_autocomplete */,
false /* prefer_keyword */,
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();
}
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::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;
return;
}
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