Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/omnibox_search_hint.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/task.h"
     10 // TODO(avi): remove when conversions not needed any more
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/autocomplete/autocomplete.h"
     13 #include "chrome/browser/autocomplete/autocomplete_edit.h"
     14 #include "chrome/browser/autocomplete/autocomplete_edit_view.h"
     15 #include "chrome/browser/autocomplete/autocomplete_match.h"
     16 #include "chrome/browser/prefs/pref_service.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/search_engines/template_url.h"
     19 #include "chrome/browser/search_engines/template_url_model.h"
     20 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
     21 #include "chrome/browser/ui/browser_list.h"
     22 #include "chrome/browser/ui/browser_window.h"
     23 #include "chrome/browser/ui/omnibox/location_bar.h"
     24 #include "chrome/common/chrome_switches.h"
     25 #include "chrome/common/pref_names.h"
     26 #include "content/browser/tab_contents/tab_contents.h"
     27 #include "content/common/notification_details.h"
     28 #include "content/common/notification_source.h"
     29 #include "content/common/notification_type.h"
     30 #include "grit/generated_resources.h"
     31 #include "grit/theme_resources.h"
     32 #include "ui/base/l10n/l10n_util.h"
     33 #include "ui/base/resource/resource_bundle.h"
     34 
     35 // The URLs of search engines for which we want to trigger the infobar.
     36 const char* kSearchEngineURLs[] = {
     37     "http://www.google.com/",
     38     "http://www.yahoo.com/",
     39     "http://www.bing.com/",
     40     "http://www.altavista.com/",
     41     "http://www.ask.com/",
     42     "http://www.wolframalpha.com/",
     43 };
     44 
     45 
     46 // HintInfoBar ----------------------------------------------------------------
     47 
     48 class HintInfoBar : public ConfirmInfoBarDelegate {
     49  public:
     50   explicit HintInfoBar(OmniboxSearchHint* omnibox_hint);
     51 
     52  private:
     53   virtual ~HintInfoBar();
     54 
     55   void AllowExpiry() { should_expire_ = true; }
     56 
     57   // ConfirmInfoBarDelegate:
     58   virtual bool ShouldExpire(
     59       const NavigationController::LoadCommittedDetails& details) const;
     60   virtual void InfoBarDismissed();
     61   virtual void InfoBarClosed();
     62   virtual SkBitmap* GetIcon() const;
     63   virtual Type GetInfoBarType() const;
     64   virtual string16 GetMessageText() const;
     65   virtual int GetButtons() const;
     66   virtual string16 GetButtonLabel(InfoBarButton button) const;
     67   virtual bool Accept();
     68 
     69   // The omnibox hint that shows us.
     70   OmniboxSearchHint* omnibox_hint_;
     71 
     72   // Whether the user clicked one of the buttons.
     73   bool action_taken_;
     74 
     75   // Whether the info-bar should be dismissed on the next navigation.
     76   bool should_expire_;
     77 
     78   // Used to delay the expiration of the info-bar.
     79   ScopedRunnableMethodFactory<HintInfoBar> method_factory_;
     80 
     81   DISALLOW_COPY_AND_ASSIGN(HintInfoBar);
     82 };
     83 
     84 HintInfoBar::HintInfoBar(OmniboxSearchHint* omnibox_hint)
     85     : ConfirmInfoBarDelegate(omnibox_hint->tab()),
     86       omnibox_hint_(omnibox_hint),
     87       action_taken_(false),
     88       should_expire_(false),
     89       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
     90   // We want the info-bar to stick-around for few seconds and then be hidden
     91   // on the next navigation after that.
     92   MessageLoop::current()->PostDelayedTask(FROM_HERE,
     93       method_factory_.NewRunnableMethod(&HintInfoBar::AllowExpiry),
     94       8000);  // 8 seconds.
     95 }
     96 
     97 HintInfoBar::~HintInfoBar() {
     98 }
     99 
    100 bool HintInfoBar::ShouldExpire(
    101     const NavigationController::LoadCommittedDetails& details) const {
    102   return should_expire_;
    103 }
    104 
    105 void HintInfoBar::InfoBarDismissed() {
    106   action_taken_ = true;
    107   UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Closed", 1);
    108   // User closed the infobar, let's not bug him again with this in the future.
    109   omnibox_hint_->DisableHint();
    110 }
    111 
    112 void HintInfoBar::InfoBarClosed() {
    113   if (!action_taken_)
    114     UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Ignored", 1);
    115   delete this;
    116 }
    117 
    118 SkBitmap* HintInfoBar::GetIcon() const {
    119   return ResourceBundle::GetSharedInstance().GetBitmapNamed(
    120      IDR_INFOBAR_QUESTION_MARK);
    121 }
    122 
    123 InfoBarDelegate::Type HintInfoBar::GetInfoBarType() const {
    124   return PAGE_ACTION_TYPE;
    125 }
    126 
    127 string16 HintInfoBar::GetMessageText() const {
    128   return l10n_util::GetStringUTF16(IDS_OMNIBOX_SEARCH_HINT_INFOBAR_TEXT);
    129 }
    130 
    131 int HintInfoBar::GetButtons() const {
    132   return BUTTON_OK;
    133 }
    134 
    135 string16 HintInfoBar::GetButtonLabel(InfoBarButton button) const {
    136   DCHECK_EQ(BUTTON_OK, button);
    137   return l10n_util::GetStringUTF16(
    138       IDS_OMNIBOX_SEARCH_HINT_INFOBAR_BUTTON_LABEL);
    139 }
    140 
    141 bool HintInfoBar::Accept() {
    142   action_taken_ = true;
    143   UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.ShowMe", 1);
    144   omnibox_hint_->DisableHint();
    145   omnibox_hint_->ShowEnteringQuery();
    146   return true;
    147 }
    148 
    149 
    150 // OmniboxSearchHint ----------------------------------------------------------
    151 
    152 OmniboxSearchHint::OmniboxSearchHint(TabContents* tab) : tab_(tab) {
    153   NavigationController* controller = &(tab->controller());
    154   notification_registrar_.Add(this,
    155                               NotificationType::NAV_ENTRY_COMMITTED,
    156                               Source<NavigationController>(controller));
    157   // Fill the search_engine_urls_ map, used for faster look-up (overkill?).
    158   for (size_t i = 0;
    159        i < sizeof(kSearchEngineURLs) / sizeof(kSearchEngineURLs[0]); ++i) {
    160     search_engine_urls_[kSearchEngineURLs[i]] = 1;
    161   }
    162 
    163   // Listen for omnibox to figure-out when the user searches from the omnibox.
    164   notification_registrar_.Add(this,
    165                               NotificationType::OMNIBOX_OPENED_URL,
    166                               Source<Profile>(tab->profile()));
    167 }
    168 
    169 OmniboxSearchHint::~OmniboxSearchHint() {
    170 }
    171 
    172 void OmniboxSearchHint::Observe(NotificationType type,
    173                                 const NotificationSource& source,
    174                                 const NotificationDetails& details) {
    175   if (type == NotificationType::NAV_ENTRY_COMMITTED) {
    176     NavigationEntry* entry = tab_->controller().GetActiveEntry();
    177     if (search_engine_urls_.find(entry->url().spec()) ==
    178         search_engine_urls_.end()) {
    179       // The search engine is not in our white-list, bail.
    180       return;
    181     }
    182     const TemplateURL* const default_provider =
    183         tab_->profile()->GetTemplateURLModel()->GetDefaultSearchProvider();
    184     if (!default_provider)
    185       return;
    186 
    187     const TemplateURLRef* const search_url = default_provider->url();
    188     if (search_url->GetHost() == entry->url().host())
    189       ShowInfoBar();
    190   } else if (type == NotificationType::OMNIBOX_OPENED_URL) {
    191     AutocompleteLog* log = Details<AutocompleteLog>(details).ptr();
    192     AutocompleteMatch::Type type =
    193         log->result.match_at(log->selected_index).type;
    194     if (type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED ||
    195         type == AutocompleteMatch::SEARCH_HISTORY ||
    196         type == AutocompleteMatch::SEARCH_SUGGEST) {
    197       // The user performed a search from the omnibox, don't show the infobar
    198       // again.
    199       DisableHint();
    200     }
    201   }
    202 }
    203 
    204 void OmniboxSearchHint::ShowInfoBar() {
    205   tab_->AddInfoBar(new HintInfoBar(this));
    206 }
    207 
    208 void OmniboxSearchHint::ShowEnteringQuery() {
    209   LocationBar* location_bar = BrowserList::GetLastActive()->window()->
    210       GetLocationBar();
    211   AutocompleteEditView*  edit_view = location_bar->location_entry();
    212   location_bar->FocusLocation(true);
    213   edit_view->SetUserText(
    214       l10n_util::GetStringUTF16(IDS_OMNIBOX_SEARCH_HINT_OMNIBOX_TEXT));
    215   edit_view->SelectAll(false);
    216   // Entering text in the autocomplete edit view triggers the suggestion popup
    217   // that we don't want to show in this case.
    218   edit_view->ClosePopup();
    219 }
    220 
    221 void OmniboxSearchHint::DisableHint() {
    222   // The NAV_ENTRY_COMMITTED notification was needed to show the infobar, the
    223   // OMNIBOX_OPENED_URL notification was there to set the kShowOmniboxSearchHint
    224   // prefs to false, none of them are needed anymore.
    225   notification_registrar_.RemoveAll();
    226   tab_->profile()->GetPrefs()->SetBoolean(prefs::kShowOmniboxSearchHint,
    227                                           false);
    228 }
    229 
    230 // static
    231 bool OmniboxSearchHint::IsEnabled(Profile* profile) {
    232   // The infobar can only be shown if the correct switch has been provided and
    233   // the user did not dismiss the infobar before.
    234   return profile->GetPrefs()->GetBoolean(prefs::kShowOmniboxSearchHint) &&
    235       CommandLine::ForCurrentProcess()->HasSwitch(
    236       switches::kSearchInOmniboxHint);
    237 }
    238