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.
5 #include "chrome/browser/ui/webui/options/search_engine_manager_handler.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/ui/search_engines/keyword_editor_controller.h"
16 #include "chrome/browser/ui/search_engines/template_url_table_model.h"
17 #include "chrome/common/url_constants.h"
18 #include "content/public/browser/user_metrics.h"
19 #include "content/public/browser/web_ui.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "extensions/common/extension.h"
22 #include "grit/generated_resources.h"
23 #include "grit/locale_settings.h"
24 #include "ui/base/l10n/l10n_util.h"
28 enum EngineInfoIndexes {
38 SearchEngineManagerHandler::SearchEngineManagerHandler() {
41 SearchEngineManagerHandler::~SearchEngineManagerHandler() {
42 if (list_controller_.get() && list_controller_->table_model())
43 list_controller_->table_model()->SetObserver(NULL);
46 void SearchEngineManagerHandler::InitializeHandler() {
47 list_controller_.reset(
48 new KeywordEditorController(Profile::FromWebUI(web_ui())));
49 DCHECK(list_controller_.get());
50 list_controller_->table_model()->SetObserver(this);
53 void SearchEngineManagerHandler::InitializePage() {
57 void SearchEngineManagerHandler::GetLocalizedValues(
58 base::DictionaryValue* localized_strings) {
59 DCHECK(localized_strings);
61 RegisterTitle(localized_strings, "searchEngineManagerPage",
62 IDS_SEARCH_ENGINES_EDITOR_WINDOW_TITLE);
63 localized_strings->SetString("defaultSearchEngineListTitle",
64 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAIN_SEPARATOR));
65 localized_strings->SetString("otherSearchEngineListTitle",
66 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_OTHER_SEPARATOR));
67 localized_strings->SetString("extensionKeywordsListTitle",
68 l10n_util::GetStringUTF16(
69 IDS_SEARCH_ENGINES_EDITOR_EXTENSIONS_SEPARATOR));
70 localized_strings->SetString("makeDefaultSearchEngineButton",
71 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON));
72 localized_strings->SetString("searchEngineTableNamePlaceholder",
73 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_NAME_PLACEHOLDER));
74 localized_strings->SetString("searchEngineTableKeywordPlaceholder",
75 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_KEYWORD_PLACEHOLDER));
76 localized_strings->SetString("searchEngineTableURLPlaceholder",
77 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINE_ADD_NEW_URL_PLACEHOLDER));
78 localized_strings->SetString("editSearchEngineInvalidTitleToolTip",
79 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_TITLE_TT));
80 localized_strings->SetString("editSearchEngineInvalidKeywordToolTip",
81 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT));
82 localized_strings->SetString("editSearchEngineInvalidURLToolTip",
83 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_INVALID_URL_TT));
86 void SearchEngineManagerHandler::RegisterMessages() {
87 web_ui()->RegisterMessageCallback(
88 "managerSetDefaultSearchEngine",
89 base::Bind(&SearchEngineManagerHandler::SetDefaultSearchEngine,
90 base::Unretained(this)));
91 web_ui()->RegisterMessageCallback(
93 base::Bind(&SearchEngineManagerHandler::RemoveSearchEngine,
94 base::Unretained(this)));
95 web_ui()->RegisterMessageCallback(
97 base::Bind(&SearchEngineManagerHandler::EditSearchEngine,
98 base::Unretained(this)));
99 web_ui()->RegisterMessageCallback(
100 "checkSearchEngineInfoValidity",
101 base::Bind(&SearchEngineManagerHandler::CheckSearchEngineInfoValidity,
102 base::Unretained(this)));
103 web_ui()->RegisterMessageCallback(
104 "searchEngineEditCancelled",
105 base::Bind(&SearchEngineManagerHandler::EditCancelled,
106 base::Unretained(this)));
107 web_ui()->RegisterMessageCallback(
108 "searchEngineEditCompleted",
109 base::Bind(&SearchEngineManagerHandler::EditCompleted,
110 base::Unretained(this)));
113 void SearchEngineManagerHandler::OnModelChanged() {
114 DCHECK(list_controller_.get());
115 if (!list_controller_->loaded())
118 // Find the default engine.
119 const TemplateURL* default_engine =
120 list_controller_->url_model()->GetDefaultSearchProvider();
121 int default_index = list_controller_->table_model()->IndexOfTemplateURL(
124 // Build the first list (default search engine options).
125 base::ListValue defaults_list;
126 int last_default_engine_index =
127 list_controller_->table_model()->last_search_engine_index();
128 for (int i = 0; i < last_default_engine_index; ++i) {
129 // Third argument is false, as the engine is not from an extension.
130 defaults_list.Append(CreateDictionaryForEngine(
131 i, i == default_index, false));
134 // Build the second list (other search templates).
135 base::ListValue others_list;
136 int last_other_engine_index =
137 list_controller_->table_model()->last_other_engine_index();
138 if (last_default_engine_index < 0)
139 last_default_engine_index = 0;
140 for (int i = last_default_engine_index; i < last_other_engine_index; ++i) {
141 others_list.Append(CreateDictionaryForEngine(i, i == default_index, false));
144 // Build the extension keywords list.
145 base::ListValue keyword_list;
146 if (last_other_engine_index < 0)
147 last_other_engine_index = 0;
148 int engine_count = list_controller_->table_model()->RowCount();
149 for (int i = last_other_engine_index; i < engine_count; ++i) {
150 keyword_list.Append(CreateDictionaryForEngine(i, i == default_index, true));
153 web_ui()->CallJavascriptFunction("SearchEngineManager.updateSearchEngineList",
154 defaults_list, others_list, keyword_list);
157 void SearchEngineManagerHandler::OnItemsChanged(int start, int length) {
161 void SearchEngineManagerHandler::OnItemsAdded(int start, int length) {
165 void SearchEngineManagerHandler::OnItemsRemoved(int start, int length) {
169 base::DictionaryValue* SearchEngineManagerHandler::CreateDictionaryForEngine(
170 int index, bool is_default, bool is_extension) {
171 TemplateURLTableModel* table_model = list_controller_->table_model();
172 const TemplateURL* template_url = list_controller_->GetTemplateURL(index);
174 base::DictionaryValue* dict = new base::DictionaryValue();
175 dict->SetString("name", template_url->short_name());
176 dict->SetString("displayName", table_model->GetText(
177 index, IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN));
178 dict->SetString("keyword", table_model->GetText(
179 index, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN));
180 dict->SetString("url", template_url->url_ref().DisplayURL());
181 dict->SetBoolean("urlLocked", template_url->prepopulate_id() > 0);
182 GURL icon_url = template_url->favicon_url();
183 if (icon_url.is_valid())
184 dict->SetString("iconURL", icon_url.spec());
185 dict->SetString("modelIndex", base::IntToString(index));
187 dict->SetBoolean("canBeRemoved",
188 list_controller_->CanRemove(template_url) && !is_extension);
189 dict->SetBoolean("canBeDefault",
190 list_controller_->CanMakeDefault(template_url) && !is_extension);
191 dict->SetBoolean("default", is_default);
192 dict->SetBoolean("canBeEdited", list_controller_->CanEdit(template_url));
193 dict->SetBoolean("isExtension", is_extension);
194 if (template_url->GetType() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION) {
195 const extensions::Extension* extension =
196 extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
197 ->GetExtensionById(template_url->GetExtensionId(),
198 extensions::ExtensionRegistry::EVERYTHING);
200 dict->Set("extension",
201 extensions::util::GetExtensionInfo(extension).release());
207 void SearchEngineManagerHandler::SetDefaultSearchEngine(
208 const base::ListValue* args) {
210 if (!ExtractIntegerValue(args, &index)) {
214 if (index < 0 || index >= list_controller_->table_model()->RowCount())
217 list_controller_->MakeDefaultTemplateURL(index);
219 content::RecordAction(
220 base::UserMetricsAction("Options_SearchEngineSetDefault"));
223 void SearchEngineManagerHandler::RemoveSearchEngine(
224 const base::ListValue* args) {
226 if (!ExtractIntegerValue(args, &index)) {
230 if (index < 0 || index >= list_controller_->table_model()->RowCount())
233 if (list_controller_->CanRemove(list_controller_->GetTemplateURL(index))) {
234 list_controller_->RemoveTemplateURL(index);
235 content::RecordAction(
236 base::UserMetricsAction("Options_SearchEngineRemoved"));
240 void SearchEngineManagerHandler::EditSearchEngine(const base::ListValue* args) {
242 if (!ExtractIntegerValue(args, &index)) {
247 // Allow -1, which means we are adding a new engine.
248 if (index < -1 || index >= list_controller_->table_model()->RowCount())
251 edit_controller_.reset(new EditSearchEngineController(
252 (index == -1) ? NULL : list_controller_->GetTemplateURL(index), this,
253 Profile::FromWebUI(web_ui())));
256 void SearchEngineManagerHandler::OnEditedKeyword(
257 TemplateURL* template_url,
258 const base::string16& title,
259 const base::string16& keyword,
260 const std::string& url) {
261 DCHECK(!url.empty());
263 list_controller_->ModifyTemplateURL(template_url, title, keyword, url);
265 list_controller_->AddTemplateURL(title, keyword, url);
266 edit_controller_.reset();
269 void SearchEngineManagerHandler::CheckSearchEngineInfoValidity(
270 const base::ListValue* args)
272 if (!edit_controller_.get())
275 base::string16 keyword;
277 std::string modelIndex;
278 if (!args->GetString(ENGINE_NAME, &name) ||
279 !args->GetString(ENGINE_KEYWORD, &keyword) ||
280 !args->GetString(ENGINE_URL, &url) ||
281 !args->GetString(3, &modelIndex)) {
286 base::DictionaryValue validity;
287 validity.SetBoolean("name", edit_controller_->IsTitleValid(name));
288 validity.SetBoolean("keyword", edit_controller_->IsKeywordValid(keyword));
289 validity.SetBoolean("url", edit_controller_->IsURLValid(url));
290 base::StringValue indexValue(modelIndex);
291 web_ui()->CallJavascriptFunction("SearchEngineManager.validityCheckCallback",
292 validity, indexValue);
295 void SearchEngineManagerHandler::EditCancelled(const base::ListValue* args) {
296 if (!edit_controller_.get())
298 edit_controller_->CleanUpCancelledAdd();
299 edit_controller_.reset();
302 void SearchEngineManagerHandler::EditCompleted(const base::ListValue* args) {
303 if (!edit_controller_.get())
306 base::string16 keyword;
308 if (!args->GetString(ENGINE_NAME, &name) ||
309 !args->GetString(ENGINE_KEYWORD, &keyword) ||
310 !args->GetString(ENGINE_URL, &url)) {
315 // Recheck validity. It's possible to get here with invalid input if e.g. the
316 // user calls the right JS functions directly from the web inspector.
317 if (edit_controller_->IsTitleValid(name) &&
318 edit_controller_->IsKeywordValid(keyword) &&
319 edit_controller_->IsURLValid(url))
320 edit_controller_->AcceptAddOrEdit(name, keyword, url);
323 } // namespace options