1 // Copyright (c) 2012 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.
5 #include "chrome/browser/omnibox_search_hint.h"
8 #include "base/command_line.h"
9 #include "base/memory/weak_ptr.h"
10 #include "base/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "chrome/browser/api/infobars/confirm_infobar_delegate.h"
13 #include "chrome/browser/autocomplete/autocomplete_log.h"
14 #include "chrome/browser/autocomplete/autocomplete_match.h"
15 #include "chrome/browser/autocomplete/autocomplete_result.h"
16 #include "chrome/browser/infobars/infobar_tab_helper.h"
17 #include "chrome/browser/prefs/pref_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/search_engines/template_url.h"
20 #include "chrome/browser/search_engines/template_url_service.h"
21 #include "chrome/browser/search_engines/template_url_service_factory.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_finder.h"
24 #include "chrome/browser/ui/browser_window.h"
25 #include "chrome/browser/ui/omnibox/location_bar.h"
26 #include "chrome/browser/ui/omnibox/omnibox_view.h"
27 #include "chrome/browser/ui/tab_contents/tab_contents.h"
28 #include "chrome/common/chrome_notification_types.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/pref_names.h"
31 #include "content/public/browser/navigation_details.h"
32 #include "content/public/browser/navigation_entry.h"
33 #include "content/public/browser/notification_details.h"
34 #include "content/public/browser/notification_source.h"
35 #include "content/public/browser/notification_types.h"
36 #include "content/public/browser/web_contents.h"
37 #include "grit/generated_resources.h"
38 #include "grit/theme_resources.h"
39 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/base/resource/resource_bundle.h"
42 using content::NavigationController
;
43 using content::NavigationEntry
;
45 DEFINE_WEB_CONTENTS_USER_DATA_KEY(OmniboxSearchHint
)
47 // The URLs of search engines for which we want to trigger the infobar.
48 const char* const kSearchEngineURLs
[] = {
49 "http://www.google.com/",
50 "http://www.yahoo.com/",
51 "http://www.bing.com/",
52 "http://www.altavista.com/",
53 "http://www.ask.com/",
54 "http://www.wolframalpha.com/",
58 // HintInfoBar ----------------------------------------------------------------
60 class HintInfoBar
: public ConfirmInfoBarDelegate
{
62 HintInfoBar(OmniboxSearchHint
* omnibox_hint
,
63 InfoBarTabHelper
* infobar_tab_helper
);
66 virtual ~HintInfoBar();
68 void AllowExpiry() { should_expire_
= true; }
70 // ConfirmInfoBarDelegate:
71 virtual void InfoBarDismissed() OVERRIDE
;
72 virtual gfx::Image
* GetIcon() const OVERRIDE
;
73 virtual Type
GetInfoBarType() const OVERRIDE
;
74 virtual string16
GetMessageText() const OVERRIDE
;
75 virtual int GetButtons() const OVERRIDE
;
76 virtual string16
GetButtonLabel(InfoBarButton button
) const OVERRIDE
;
77 virtual bool Accept() OVERRIDE
;
78 virtual bool ShouldExpireInternal(
79 const content::LoadCommittedDetails
& details
) const OVERRIDE
;
81 // The omnibox hint that shows us.
82 OmniboxSearchHint
* omnibox_hint_
;
84 // Whether the user clicked one of the buttons.
87 // Whether the info-bar should be dismissed on the next navigation.
90 // Used to delay the expiration of the info-bar.
91 base::WeakPtrFactory
<HintInfoBar
> weak_factory_
;
93 DISALLOW_COPY_AND_ASSIGN(HintInfoBar
);
96 HintInfoBar::HintInfoBar(OmniboxSearchHint
* omnibox_hint
,
97 InfoBarTabHelper
* infobar_tab_helper
)
98 : ConfirmInfoBarDelegate(infobar_tab_helper
),
99 omnibox_hint_(omnibox_hint
),
100 action_taken_(false),
101 should_expire_(false),
102 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
103 // We want the info-bar to stick-around for few seconds and then be hidden
104 // on the next navigation after that.
105 MessageLoop::current()->PostDelayedTask(
107 base::Bind(&HintInfoBar::AllowExpiry
, weak_factory_
.GetWeakPtr()),
108 base::TimeDelta::FromSeconds(8));
111 HintInfoBar::~HintInfoBar() {
113 UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Ignored", 1);
116 void HintInfoBar::InfoBarDismissed() {
117 action_taken_
= true;
118 UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Closed", 1);
119 // User closed the infobar, let's not bug him again with this in the future.
120 omnibox_hint_
->DisableHint();
123 gfx::Image
* HintInfoBar::GetIcon() const {
124 return &ResourceBundle::GetSharedInstance().GetNativeImageNamed(
125 IDR_INFOBAR_QUESTION_MARK
);
128 InfoBarDelegate::Type
HintInfoBar::GetInfoBarType() const {
129 return PAGE_ACTION_TYPE
;
132 string16
HintInfoBar::GetMessageText() const {
133 return l10n_util::GetStringUTF16(IDS_OMNIBOX_SEARCH_HINT_INFOBAR_TEXT
);
136 int HintInfoBar::GetButtons() const {
140 string16
HintInfoBar::GetButtonLabel(InfoBarButton button
) const {
141 DCHECK_EQ(BUTTON_OK
, button
);
142 return l10n_util::GetStringUTF16(
143 IDS_OMNIBOX_SEARCH_HINT_INFOBAR_BUTTON_LABEL
);
146 bool HintInfoBar::Accept() {
147 action_taken_
= true;
148 UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.ShowMe", 1);
149 omnibox_hint_
->DisableHint();
150 omnibox_hint_
->ShowEnteringQuery();
154 bool HintInfoBar::ShouldExpireInternal(
155 const content::LoadCommittedDetails
& details
) const {
156 return should_expire_
;
160 // OmniboxSearchHint ----------------------------------------------------------
162 OmniboxSearchHint::OmniboxSearchHint(content::WebContents
* web_contents
)
163 : web_contents_(web_contents
) {
164 NavigationController
* controller
= &(web_contents
->GetController());
165 notification_registrar_
.Add(
167 content::NOTIFICATION_NAV_ENTRY_COMMITTED
,
168 content::Source
<NavigationController
>(controller
));
169 // Fill the search_engine_urls_ map, used for faster look-up (overkill?).
170 for (size_t i
= 0; i
< arraysize(kSearchEngineURLs
); ++i
)
171 search_engine_urls_
[kSearchEngineURLs
[i
]] = 1;
174 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
175 // Listen for omnibox to figure-out when the user searches from the omnibox.
176 notification_registrar_
.Add(this,
177 chrome::NOTIFICATION_OMNIBOX_OPENED_URL
,
178 content::Source
<Profile
>(profile
));
181 OmniboxSearchHint::~OmniboxSearchHint() {
184 void OmniboxSearchHint::Observe(int type
,
185 const content::NotificationSource
& source
,
186 const content::NotificationDetails
& details
) {
187 if (type
== content::NOTIFICATION_NAV_ENTRY_COMMITTED
) {
188 content::NavigationEntry
* entry
=
189 web_contents_
->GetController().GetActiveEntry();
190 if (search_engine_urls_
.find(entry
->GetURL().spec()) ==
191 search_engine_urls_
.end()) {
192 // The search engine is not in our white-list, bail.
196 Profile::FromBrowserContext(web_contents_
->GetBrowserContext());
197 const TemplateURL
* const default_provider
=
198 TemplateURLServiceFactory::GetForProfile(profile
)->
199 GetDefaultSearchProvider();
200 if (!default_provider
)
203 if (default_provider
->url_ref().GetHost() == entry
->GetURL().host())
205 } else if (type
== chrome::NOTIFICATION_OMNIBOX_OPENED_URL
) {
206 AutocompleteLog
* log
= content::Details
<AutocompleteLog
>(details
).ptr();
207 AutocompleteMatch::Type type
=
208 log
->result
.match_at(log
->selected_index
).type
;
209 if (AutocompleteMatch::IsSearchType(type
)) {
210 // The user performed a search from the omnibox, don't show the infobar
217 void OmniboxSearchHint::ShowInfoBar() {
218 InfoBarTabHelper
* infobar_tab_helper
=
219 InfoBarTabHelper::FromWebContents(web_contents_
);
220 infobar_tab_helper
->AddInfoBar(new HintInfoBar(this, infobar_tab_helper
));
223 void OmniboxSearchHint::ShowEnteringQuery() {
224 LocationBar
* location_bar
= browser::FindBrowserWithWebContents(
225 web_contents_
)->window()->GetLocationBar();
226 OmniboxView
* omnibox_view
= location_bar
->GetLocationEntry();
227 location_bar
->FocusLocation(true);
228 omnibox_view
->SetUserText(
229 l10n_util::GetStringUTF16(IDS_OMNIBOX_SEARCH_HINT_OMNIBOX_TEXT
));
230 omnibox_view
->SelectAll(false);
231 // Entering text in the omnibox view triggers the suggestion popup that we
232 // don't want to show in this case.
233 omnibox_view
->CloseOmniboxPopup();
236 void OmniboxSearchHint::DisableHint() {
237 // The NAV_ENTRY_COMMITTED notification was needed to show the infobar, the
238 // OMNIBOX_OPENED_URL notification was there to set the kShowOmniboxSearchHint
239 // prefs to false, none of them are needed anymore.
240 notification_registrar_
.RemoveAll();
242 Profile::FromBrowserContext(web_contents_
->GetBrowserContext());
243 profile
->GetPrefs()->SetBoolean(prefs::kShowOmniboxSearchHint
, false);
247 bool OmniboxSearchHint::IsEnabled(Profile
* profile
) {
248 // The infobar can only be shown if the correct switch has been provided and
249 // the user did not dismiss the infobar before.
250 return profile
->GetPrefs()->GetBoolean(prefs::kShowOmniboxSearchHint
) &&
251 CommandLine::ForCurrentProcess()->HasSwitch(
252 switches::kSearchInOmniboxHint
);