blob: 28db8a9611f522bef3354168db00f2410cd93f6a [file] [log] [blame]
[email protected]a26dc362010-04-23 01:48:581// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]c8865482009-07-23 20:40:102// 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/animation.h"
8#include "app/l10n_util.h"
9#include "app/resource_bundle.h"
10#include "base/command_line.h"
11#include "base/task.h"
[email protected]12802702010-07-09 19:43:0912#include "chrome/browser/autocomplete/autocomplete_edit.h"
[email protected]052313b2010-02-19 09:43:0813#include "chrome/browser/autocomplete/autocomplete_edit_view.h"
[email protected]c8865482009-07-23 20:40:1014#include "chrome/browser/browser_list.h"
15#include "chrome/browser/browser_window.h"
16#include "chrome/browser/location_bar.h"
[email protected]052313b2010-02-19 09:43:0817#include "chrome/browser/pref_service.h"
[email protected]c8865482009-07-23 20:40:1018#include "chrome/browser/profile.h"
19#include "chrome/browser/search_engines/template_url_model.h"
20#include "chrome/browser/tab_contents/infobar_delegate.h"
21#include "chrome/browser/tab_contents/tab_contents.h"
22#include "chrome/common/chrome_switches.h"
23#include "chrome/common/notification_service.h"
24#include "chrome/common/notification_type.h"
25#include "chrome/common/pref_names.h"
26#include "grit/generated_resources.h"
[email protected]1db6ff152009-10-12 15:32:0727#include "grit/theme_resources.h"
[email protected]c8865482009-07-23 20:40:1028
29// The URLs of search engines for which we want to trigger the infobar.
30const char* kSearchEngineURLs[] = {
31 "http://www.google.com/",
32 "http://www.yahoo.com/",
33 "http://www.bing.com/",
34 "http://www.altavista.com/",
35 "http://www.ask.com/",
36 "http://www.wolframalpha.com/",
37};
38
39class HintInfoBar : public ConfirmInfoBarDelegate {
40 public:
41 explicit HintInfoBar(OmniboxSearchHint* omnibox_hint)
42 : ConfirmInfoBarDelegate(omnibox_hint->tab()),
43 omnibox_hint_(omnibox_hint),
44 action_taken_(false),
45 should_expire_(false),
46 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
47 // We want the info-bar to stick-around for few seconds and then be hidden
48 // on the next navigation after that.
49 MessageLoop::current()->PostDelayedTask(FROM_HERE,
50 method_factory_.NewRunnableMethod(&HintInfoBar::Expire),
51 8000); // 8 seconds.
52 }
53
54 virtual bool ShouldExpire(
55 const NavigationController::LoadCommittedDetails& details) const {
56 return should_expire_;
57 }
58
59 // Overridden from ConfirmInfoBarDelegate:
60 virtual void InfoBarClosed() {
61 if (!action_taken_)
62 UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Ignored", 1);
63 delete this;
64 }
65
66 virtual void InfoBarDismissed() {
67 action_taken_ = true;
68 UMA_HISTOGRAM_COUNTS("OmniboxSearchHint.Closed", 1);
69 // User closed the infobar, let's not bug him again with this in the future.
70 omnibox_hint_->DisableHint();
71 }
72
73 virtual std::wstring GetMessageText() const {
74 return l10n_util::GetString(IDS_OMNIBOX_SEARCH_HINT_INFOBAR_TEXT);
75 }
76
77 virtual SkBitmap* GetIcon() const {
78 return ResourceBundle::GetSharedInstance().GetBitmapNamed(
79 IDR_INFOBAR_QUESTION_MARK);
80 }
81
82 virtual int GetButtons() const {
83 return BUTTON_OK;
84 }
85
86 virtual std::wstring GetButtonLabel(InfoBarButton button) const {
87 return l10n_util::GetString(IDS_OMNIBOX_SEARCH_HINT_INFOBAR_BUTTON_LABEL);
88 }
89
90 virtual Type GetInfoBarType() {
91 return INFO_TYPE;
92 }
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();
[email protected]a26dc362010-04-23 01:48:58182 location_bar->FocusLocation(true);
[email protected]c8865482009-07-23 20:40:10183 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}