blob: ec57075f2ebca6a6722cdc8f7e6fc44625a7231b [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.
#include "chrome/browser/omnibox_search_hint.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/autocomplete/autocomplete_log.h"
#include "chrome/browser/autocomplete/autocomplete_match.h"
#include "chrome/browser/autocomplete/autocomplete_result.h"
#include "chrome/browser/infobars/confirm_infobar_delegate.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/omnibox/location_bar.h"
#include "chrome/browser/ui/omnibox/omnibox_view.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/web_contents.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
using content::NavigationController;
using content::NavigationEntry;
// HintInfoBarDelegate ---------------------------------------------------------
class HintInfoBarDelegate : public ConfirmInfoBarDelegate {
public:
// If the active entry for |web_contents| is a navigation to the user's
// default search engine, and the engine is on a small whitelist, creates a
// "you can search from the omnibox" hint delegate and adds it to the
// InfoBarService for |web_contents|.
static void Create(content::WebContents* web_contents,
OmniboxSearchHint* omnibox_hint);
private:
HintInfoBarDelegate(OmniboxSearchHint* omnibox_hint,
InfoBarService* infobar_service);
virtual ~HintInfoBarDelegate();
void AllowExpiry() { should_expire_ = true; }
// ConfirmInfoBarDelegate:
virtual void InfoBarDismissed() OVERRIDE;
virtual gfx::Image* GetIcon() const OVERRIDE;
virtual Type GetInfoBarType() const OVERRIDE;
virtual string16 GetMessageText() const OVERRIDE;
virtual int GetButtons() const OVERRIDE;
virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
virtual bool Accept() OVERRIDE;
virtual bool ShouldExpireInternal(
const content::LoadCommittedDetails& details) const OVERRIDE;
// The omnibox hint that shows us.
OmniboxSearchHint* omnibox_hint_;
// Whether the user clicked one of the buttons.
bool action_taken_;
// Whether the info-bar should be dismissed on the next navigation.
bool should_expire_;
// Used to delay the expiration of the info-bar.
base::WeakPtrFactory<HintInfoBarDelegate> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(HintInfoBarDelegate);
};
// static
void HintInfoBarDelegate::Create(content::WebContents* web_contents,
OmniboxSearchHint* omnibox_hint) {
// The URLs of search engines for which we want to trigger the infobar.
const char* const kSearchEngineURLs[] = {
"http://www.google.com/",
"http://www.yahoo.com/",
"http://www.bing.com/",
"http://www.altavista.com/",
"http://www.ask.com/",
"http://www.wolframalpha.com/",
};
CR_DEFINE_STATIC_LOCAL(std::set<std::string>, search_engine_urls, ());
if (search_engine_urls.empty()) {
for (size_t i = 0; i < arraysize(kSearchEngineURLs); ++i)
search_engine_urls.insert(kSearchEngineURLs[i]);
}
content::NavigationEntry* entry =
web_contents->GetController().GetActiveEntry();
if (search_engine_urls.find(entry->GetURL().spec()) ==
search_engine_urls.end()) {
// The search engine is not in our white-list, bail.
return;
}
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
const TemplateURL* const default_provider =
TemplateURLServiceFactory::GetForProfile(profile)->
GetDefaultSearchProvider();
if (!default_provider)
return;
if (default_provider->url_ref().GetHost() == entry->GetURL().host()) {
InfoBarService* infobar_service =
InfoBarService::FromWebContents(web_contents);
infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
new HintInfoBarDelegate(omnibox_hint, infobar_service)));
}
}
HintInfoBarDelegate::HintInfoBarDelegate(OmniboxSearchHint* omnibox_hint,
InfoBarService* infobar_service)
: ConfirmInfoBarDelegate(infobar_service),
omnibox_hint_(omnibox_hint),
action_taken_(false),
should_expire_(false),
weak_factory_(this) {
// We want the info-bar to stick-around for few seconds and then be hidden
// on the next navigation after that.
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&HintInfoBarDelegate::AllowExpiry, weak_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(8));
}
HintInfoBarDelegate::~HintInfoBarDelegate() {
if (!action_taken_)
UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Ignored", 1);
}
void HintInfoBarDelegate::InfoBarDismissed() {
action_taken_ = true;
UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Closed", 1);
// User closed the infobar, let's not bug him again with this in the future.
omnibox_hint_->DisableHint();
}
gfx::Image* HintInfoBarDelegate::GetIcon() const {
return &ResourceBundle::GetSharedInstance().GetNativeImageNamed(
IDR_INFOBAR_QUESTION_MARK);
}
InfoBarDelegate::Type HintInfoBarDelegate::GetInfoBarType() const {
return PAGE_ACTION_TYPE;
}
string16 HintInfoBarDelegate::GetMessageText() const {
return l10n_util::GetStringUTF16(IDS_OMNIBOX_SEARCH_HINT_INFOBAR_TEXT);
}
int HintInfoBarDelegate::GetButtons() const {
return BUTTON_OK;
}
string16 HintInfoBarDelegate::GetButtonLabel(InfoBarButton button) const {
DCHECK_EQ(BUTTON_OK, button);
return l10n_util::GetStringUTF16(
IDS_OMNIBOX_SEARCH_HINT_INFOBAR_BUTTON_LABEL);
}
bool HintInfoBarDelegate::Accept() {
action_taken_ = true;
UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.ShowMe", 1);
omnibox_hint_->DisableHint();
omnibox_hint_->ShowEnteringQuery();
return true;
}
bool HintInfoBarDelegate::ShouldExpireInternal(
const content::LoadCommittedDetails& details) const {
return should_expire_;
}
// OmniboxSearchHint ----------------------------------------------------------
DEFINE_WEB_CONTENTS_USER_DATA_KEY(OmniboxSearchHint);
OmniboxSearchHint::OmniboxSearchHint(content::WebContents* web_contents)
: web_contents_(web_contents) {
NavigationController* controller = &(web_contents->GetController());
notification_registrar_.Add(
this,
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::Source<NavigationController>(controller));
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
// Listen for omnibox to figure-out when the user searches from the omnibox.
notification_registrar_.Add(this,
chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
content::Source<Profile>(profile));
}
OmniboxSearchHint::~OmniboxSearchHint() {
}
void OmniboxSearchHint::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) {
HintInfoBarDelegate::Create(web_contents_, this);
} else if (type == chrome::NOTIFICATION_OMNIBOX_OPENED_URL) {
AutocompleteLog* log = content::Details<AutocompleteLog>(details).ptr();
AutocompleteMatch::Type type =
log->result.match_at(log->selected_index).type;
if (AutocompleteMatch::IsSearchType(type)) {
// The user performed a search from the omnibox, don't show the infobar
// again.
DisableHint();
}
}
}
void OmniboxSearchHint::ShowEnteringQuery() {
LocationBar* location_bar = chrome::FindBrowserWithWebContents(
web_contents_)->window()->GetLocationBar();
OmniboxView* omnibox_view = location_bar->GetLocationEntry();
location_bar->FocusLocation(true);
omnibox_view->SetUserText(
l10n_util::GetStringUTF16(IDS_OMNIBOX_SEARCH_HINT_OMNIBOX_TEXT));
omnibox_view->SelectAll(false);
// Entering text in the omnibox view triggers the suggestion popup that we
// don't want to show in this case.
omnibox_view->CloseOmniboxPopup();
}
void OmniboxSearchHint::DisableHint() {
// The NAV_ENTRY_COMMITTED notification was needed to show the infobar, the
// OMNIBOX_OPENED_URL notification was there to set the kShowOmniboxSearchHint
// prefs to false, none of them are needed anymore.
notification_registrar_.RemoveAll();
Profile* profile =
Profile::FromBrowserContext(web_contents_->GetBrowserContext());
profile->GetPrefs()->SetBoolean(prefs::kShowOmniboxSearchHint, false);
}
// static
bool OmniboxSearchHint::IsEnabled(Profile* profile) {
// The infobar can only be shown if the correct switch has been provided and
// the user did not dismiss the infobar before.
return profile->GetPrefs()->GetBoolean(prefs::kShowOmniboxSearchHint) &&
CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSearchInOmniboxHint);
}