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