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