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