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