1 // Copyright (c) 2012 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/ui/webui/options/search_engine_manager_handler.h"
6
7 #include "base/bind.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/search_engines/template_url.h"
14 #include "chrome/browser/search_engines/template_url_service.h"
15 #include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
16 #include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
17 #include "chrome/browser/ui/search_engines/template_url_table_model.h"
18 #include "chrome/common/url_constants.h"
19 #include "content/public/browser/user_metrics.h"
20 #include "content/public/browser/web_ui.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/common/extension.h"
23 #include "grit/generated_resources.h"
24 #include "grit/locale_settings.h"
25 #include "ui/base/l10n/l10n_util.h"
26
27 namespace {
28
29 enum EngineInfoIndexes {
30 ENGINE_NAME,
31 ENGINE_KEYWORD,
32 ENGINE_URL,
33 };
34
35 }; // namespace
36
37 namespace options {
38
SearchEngineManagerHandler()39 SearchEngineManagerHandler::SearchEngineManagerHandler() {
40 }
41
~SearchEngineManagerHandler()42 SearchEngineManagerHandler::~SearchEngineManagerHandler() {
43 if (list_controller_.get() && list_controller_->table_model())
44 list_controller_->table_model()->SetObserver(NULL);
45 }
46
InitializeHandler()47 void SearchEngineManagerHandler::InitializeHandler() {
48 list_controller_.reset(
49 new KeywordEditorController(Profile::FromWebUI(web_ui())));
50 DCHECK(list_controller_.get());
51 list_controller_->table_model()->SetObserver(this);
52 }
53
InitializePage()54 void SearchEngineManagerHandler::InitializePage() {
55 OnModelChanged();
56 }
57
GetLocalizedValues(base::DictionaryValue * localized_strings)58 void SearchEngineManagerHandler::GetLocalizedValues(
59 base::DictionaryValue* localized_strings) {
60 DCHECK(localized_strings);
61
62 RegisterTitle(localized_strings, "searchEngineManagerPage",
63 IDS_SEARCH_ENGINES_EDITOR_WINDOW_TITLE);
64 localized_strings->SetString("defaultSearchEngineListTitle",
65 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAIN_SEPARATOR));
66 localized_strings->SetString("otherSearchEngineListTitle",
67 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_OTHER_SEPARATOR));
68 localized_strings->SetString("extensionKeywordsListTitle",
69 l10n_util::GetStringUTF16(
70 IDS_SEARCH_ENGINES_EDITOR_EXTENSIONS_SEPARATOR));
71 localized_strings->SetString("makeDefaultSearchEngineButton",
72 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON));
73 localized_strings->SetString("searchEngineTableNamePlaceholder",
74 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_NAME_PLACEHOLDER));
75 localized_strings->SetString("searchEngineTableKeywordPlaceholder",
76 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_KEYWORD_PLACEHOLDER));
77 localized_strings->SetString("searchEngineTableURLPlaceholder",
78 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_URL_PLACEHOLDER));
79 localized_strings->SetString("editSearchEngineInvalidTitleToolTip",
80 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_TITLE_TT));
81 localized_strings->SetString("editSearchEngineInvalidKeywordToolTip",
82 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT));
83 localized_strings->SetString("editSearchEngineInvalidURLToolTip",
84 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_URL_TT));
85 }
86
RegisterMessages()87 void SearchEngineManagerHandler::RegisterMessages() {
88 web_ui()->RegisterMessageCallback(
89 "managerSetDefaultSearchEngine",
90 base::Bind(&SearchEngineManagerHandler::SetDefaultSearchEngine,
91 base::Unretained(this)));
92 web_ui()->RegisterMessageCallback(
93 "removeSearchEngine",
94 base::Bind(&SearchEngineManagerHandler::RemoveSearchEngine,
95 base::Unretained(this)));
96 web_ui()->RegisterMessageCallback(
97 "editSearchEngine",
98 base::Bind(&SearchEngineManagerHandler::EditSearchEngine,
99 base::Unretained(this)));
100 web_ui()->RegisterMessageCallback(
101 "checkSearchEngineInfoValidity",
102 base::Bind(&SearchEngineManagerHandler::CheckSearchEngineInfoValidity,
103 base::Unretained(this)));
104 web_ui()->RegisterMessageCallback(
105 "searchEngineEditCancelled",
106 base::Bind(&SearchEngineManagerHandler::EditCancelled,
107 base::Unretained(this)));
108 web_ui()->RegisterMessageCallback(
109 "searchEngineEditCompleted",
110 base::Bind(&SearchEngineManagerHandler::EditCompleted,
111 base::Unretained(this)));
112 }
113
OnModelChanged()114 void SearchEngineManagerHandler::OnModelChanged() {
115 DCHECK(list_controller_.get());
116 if (!list_controller_->loaded())
117 return;
118
119 // Find the default engine.
120 const TemplateURL* default_engine =
121 list_controller_->url_model()->GetDefaultSearchProvider();
122 int default_index = list_controller_->table_model()->IndexOfTemplateURL(
123 default_engine);
124
125 // Build the first list (default search engine options).
126 base::ListValue defaults_list;
127 int last_default_engine_index =
128 list_controller_->table_model()->last_search_engine_index();
129 for (int i = 0; i < last_default_engine_index; ++i) {
130 // Third argument is false, as the engine is not from an extension.
131 defaults_list.Append(CreateDictionaryForEngine(
132 i, i == default_index, false));
133 }
134
135 // Build the second list (other search templates).
136 base::ListValue others_list;
137 int last_other_engine_index =
138 list_controller_->table_model()->last_other_engine_index();
139 if (last_default_engine_index < 0)
140 last_default_engine_index = 0;
141 for (int i = last_default_engine_index; i < last_other_engine_index; ++i) {
142 others_list.Append(CreateDictionaryForEngine(i, i == default_index, false));
143 }
144
145 // Build the extension keywords list.
146 base::ListValue keyword_list;
147 if (last_other_engine_index < 0)
148 last_other_engine_index = 0;
149 int engine_count = list_controller_->table_model()->RowCount();
150 for (int i = last_other_engine_index; i < engine_count; ++i) {
151 keyword_list.Append(CreateDictionaryForEngine(i, i == default_index, true));
152 }
153
154 web_ui()->CallJavascriptFunction("SearchEngineManager.updateSearchEngineList",
155 defaults_list, others_list, keyword_list);
156 }
157
OnItemsChanged(int start,int length)158 void SearchEngineManagerHandler::OnItemsChanged(int start, int length) {
159 OnModelChanged();
160 }
161
OnItemsAdded(int start,int length)162 void SearchEngineManagerHandler::OnItemsAdded(int start, int length) {
163 OnModelChanged();
164 }
165
OnItemsRemoved(int start,int length)166 void SearchEngineManagerHandler::OnItemsRemoved(int start, int length) {
167 OnModelChanged();
168 }
169
CreateDictionaryForEngine(int index,bool is_default,bool is_extension)170 base::DictionaryValue* SearchEngineManagerHandler::CreateDictionaryForEngine(
171 int index, bool is_default, bool is_extension) {
172 TemplateURLTableModel* table_model = list_controller_->table_model();
173 const TemplateURL* template_url = list_controller_->GetTemplateURL(index);
174
175 base::DictionaryValue* dict = new base::DictionaryValue();
176 dict->SetString("name", template_url->short_name());
177 dict->SetString("displayName", table_model->GetText(
178 index, IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN));
179 dict->SetString("keyword", table_model->GetText(
180 index, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN));
181 dict->SetString("url", template_url->url_ref().DisplayURL(
182 UIThreadSearchTermsData(Profile::FromWebUI(web_ui()))));
183 dict->SetBoolean("urlLocked", template_url->prepopulate_id() > 0);
184 GURL icon_url = template_url->favicon_url();
185 if (icon_url.is_valid())
186 dict->SetString("iconURL", icon_url.spec());
187 dict->SetString("modelIndex", base::IntToString(index));
188
189 dict->SetBoolean("canBeRemoved",
190 list_controller_->CanRemove(template_url) && !is_extension);
191 dict->SetBoolean("canBeDefault",
192 list_controller_->CanMakeDefault(template_url) && !is_extension);
193 dict->SetBoolean("default", is_default);
194 dict->SetBoolean("canBeEdited", list_controller_->CanEdit(template_url));
195 dict->SetBoolean("isExtension", is_extension);
196 if (template_url->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) {
197 const extensions::Extension* extension =
198 extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
199 ->GetExtensionById(template_url->GetExtensionId(),
200 extensions::ExtensionRegistry::EVERYTHING);
201 if (extension) {
202 dict->Set("extension",
203 extensions::util::GetExtensionInfo(extension).release());
204 }
205 }
206 return dict;
207 }
208
SetDefaultSearchEngine(const base::ListValue * args)209 void SearchEngineManagerHandler::SetDefaultSearchEngine(
210 const base::ListValue* args) {
211 int index;
212 if (!ExtractIntegerValue(args, &index)) {
213 NOTREACHED();
214 return;
215 }
216 if (index < 0 || index >= list_controller_->table_model()->RowCount())
217 return;
218
219 list_controller_->MakeDefaultTemplateURL(index);
220
221 content::RecordAction(
222 base::UserMetricsAction("Options_SearchEngineSetDefault"));
223 }
224
RemoveSearchEngine(const base::ListValue * args)225 void SearchEngineManagerHandler::RemoveSearchEngine(
226 const base::ListValue* args) {
227 int index;
228 if (!ExtractIntegerValue(args, &index)) {
229 NOTREACHED();
230 return;
231 }
232 if (index < 0 || index >= list_controller_->table_model()->RowCount())
233 return;
234
235 if (list_controller_->CanRemove(list_controller_->GetTemplateURL(index))) {
236 list_controller_->RemoveTemplateURL(index);
237 content::RecordAction(
238 base::UserMetricsAction("Options_SearchEngineRemoved"));
239 }
240 }
241
EditSearchEngine(const base::ListValue * args)242 void SearchEngineManagerHandler::EditSearchEngine(const base::ListValue* args) {
243 int index;
244 if (!ExtractIntegerValue(args, &index)) {
245 NOTREACHED();
246 return;
247 }
248
249 // Allow -1, which means we are adding a new engine.
250 if (index < -1 || index >= list_controller_->table_model()->RowCount())
251 return;
252
253 edit_controller_.reset(new EditSearchEngineController(
254 (index == -1) ? NULL : list_controller_->GetTemplateURL(index), this,
255 Profile::FromWebUI(web_ui())));
256 }
257
OnEditedKeyword(TemplateURL * template_url,const base::string16 & title,const base::string16 & keyword,const std::string & url)258 void SearchEngineManagerHandler::OnEditedKeyword(
259 TemplateURL* template_url,
260 const base::string16& title,
261 const base::string16& keyword,
262 const std::string& url) {
263 DCHECK(!url.empty());
264 if (template_url)
265 list_controller_->ModifyTemplateURL(template_url, title, keyword, url);
266 else
267 list_controller_->AddTemplateURL(title, keyword, url);
268 edit_controller_.reset();
269 }
270
CheckSearchEngineInfoValidity(const base::ListValue * args)271 void SearchEngineManagerHandler::CheckSearchEngineInfoValidity(
272 const base::ListValue* args)
273 {
274 if (!edit_controller_.get())
275 return;
276 base::string16 name;
277 base::string16 keyword;
278 std::string url;
279 std::string modelIndex;
280 if (!args->GetString(ENGINE_NAME, &name) ||
281 !args->GetString(ENGINE_KEYWORD, &keyword) ||
282 !args->GetString(ENGINE_URL, &url) ||
283 !args->GetString(3, &modelIndex)) {
284 NOTREACHED();
285 return;
286 }
287
288 base::DictionaryValue validity;
289 validity.SetBoolean("name", edit_controller_->IsTitleValid(name));
290 validity.SetBoolean("keyword", edit_controller_->IsKeywordValid(keyword));
291 validity.SetBoolean("url", edit_controller_->IsURLValid(url));
292 base::StringValue indexValue(modelIndex);
293 web_ui()->CallJavascriptFunction("SearchEngineManager.validityCheckCallback",
294 validity, indexValue);
295 }
296
EditCancelled(const base::ListValue * args)297 void SearchEngineManagerHandler::EditCancelled(const base::ListValue* args) {
298 if (!edit_controller_.get())
299 return;
300 edit_controller_->CleanUpCancelledAdd();
301 edit_controller_.reset();
302 }
303
EditCompleted(const base::ListValue * args)304 void SearchEngineManagerHandler::EditCompleted(const base::ListValue* args) {
305 if (!edit_controller_.get())
306 return;
307 base::string16 name;
308 base::string16 keyword;
309 std::string url;
310 if (!args->GetString(ENGINE_NAME, &name) ||
311 !args->GetString(ENGINE_KEYWORD, &keyword) ||
312 !args->GetString(ENGINE_URL, &url)) {
313 NOTREACHED();
314 return;
315 }
316
317 // Recheck validity. It's possible to get here with invalid input if e.g. the
318 // user calls the right JS functions directly from the web inspector.
319 if (edit_controller_->IsTitleValid(name) &&
320 edit_controller_->IsKeywordValid(keyword) &&
321 edit_controller_->IsURLValid(url))
322 edit_controller_->AcceptAddOrEdit(name, keyword, url);
323 }
324
325 } // namespace options
326