| // 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); |
| } |