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
AllowExpiry()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
HintInfoBar(OmniboxSearchHint * omnibox_hint)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
~HintInfoBar()97 HintInfoBar::~HintInfoBar() {
98 }
99
ShouldExpire(const NavigationController::LoadCommittedDetails & details) const100 bool HintInfoBar::ShouldExpire(
101 const NavigationController::LoadCommittedDetails& details) const {
102 return should_expire_;
103 }
104
InfoBarDismissed()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
InfoBarClosed()112 void HintInfoBar::InfoBarClosed() {
113 if (!action_taken_)
114 UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Ignored", 1);
115 delete this;
116 }
117
GetIcon() const118 SkBitmap* HintInfoBar::GetIcon() const {
119 return ResourceBundle::GetSharedInstance().GetBitmapNamed(
120 IDR_INFOBAR_QUESTION_MARK);
121 }
122
GetInfoBarType() const123 InfoBarDelegate::Type HintInfoBar::GetInfoBarType() const {
124 return PAGE_ACTION_TYPE;
125 }
126
GetMessageText() const127 string16 HintInfoBar::GetMessageText() const {
128 return l10n_util::GetStringUTF16(IDS_OMNIBOX_SEARCH_HINT_INFOBAR_TEXT);
129 }
130
GetButtons() const131 int HintInfoBar::GetButtons() const {
132 return BUTTON_OK;
133 }
134
GetButtonLabel(InfoBarButton button) const135 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
Accept()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
OmniboxSearchHint(TabContents * tab)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
~OmniboxSearchHint()169 OmniboxSearchHint::~OmniboxSearchHint() {
170 }
171
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
ShowInfoBar()204 void OmniboxSearchHint::ShowInfoBar() {
205 tab_->AddInfoBar(new HintInfoBar(this));
206 }
207
ShowEnteringQuery()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
DisableHint()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
IsEnabled(Profile * profile)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