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