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/ui_thread_search_terms_data.h"
14 #include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
15 #include "chrome/browser/ui/search_engines/template_url_table_model.h"
16 #include "chrome/common/url_constants.h"
17 #include "chrome/grit/generated_resources.h"
18 #include "chrome/grit/locale_settings.h"
19 #include "components/search_engines/template_url.h"
20 #include "components/search_engines/template_url_service.h"
21 #include "content/public/browser/user_metrics.h"
22 #include "content/public/browser/web_ui.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/common/extension.h"
25 #include "ui/base/l10n/l10n_util.h"
29 enum EngineInfoIndexes
{
39 SearchEngineManagerHandler::SearchEngineManagerHandler() {
42 SearchEngineManagerHandler::~SearchEngineManagerHandler() {
43 if (list_controller_
.get() && list_controller_
->table_model())
44 list_controller_
->table_model()->SetObserver(NULL
);
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);
54 void SearchEngineManagerHandler::InitializePage() {
58 void SearchEngineManagerHandler::GetLocalizedValues(
59 base::DictionaryValue
* localized_strings
) {
60 DCHECK(localized_strings
);
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
));
87 void SearchEngineManagerHandler::RegisterMessages() {
88 web_ui()->RegisterMessageCallback(
89 "managerSetDefaultSearchEngine",
90 base::Bind(&SearchEngineManagerHandler::SetDefaultSearchEngine
,
91 base::Unretained(this)));
92 web_ui()->RegisterMessageCallback(
94 base::Bind(&SearchEngineManagerHandler::RemoveSearchEngine
,
95 base::Unretained(this)));
96 web_ui()->RegisterMessageCallback(
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)));
114 void SearchEngineManagerHandler::OnModelChanged() {
115 DCHECK(list_controller_
.get());
116 if (!list_controller_
->loaded())
119 // Find the default engine.
120 const TemplateURL
* default_engine
=
121 list_controller_
->GetDefaultSearchProvider();
122 int default_index
= list_controller_
->table_model()->IndexOfTemplateURL(
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
));
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
));
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
));
154 web_ui()->CallJavascriptFunction("SearchEngineManager.updateSearchEngineList",
155 defaults_list
, others_list
, keyword_list
);
158 void SearchEngineManagerHandler::OnItemsChanged(int start
, int length
) {
162 void SearchEngineManagerHandler::OnItemsAdded(int start
, int length
) {
166 void SearchEngineManagerHandler::OnItemsRemoved(int start
, int length
) {
170 base::DictionaryValue
* SearchEngineManagerHandler::CreateDictionaryForEngine(
171 int index
, bool is_default
) {
172 TemplateURLTableModel
* table_model
= list_controller_
->table_model();
173 const TemplateURL
* template_url
= list_controller_
->GetTemplateURL(index
);
175 // The items which are to be written into |dict| are also described in
176 // chrome/browser/resources/options/search_engine_manager_engine_list.js
177 // in @typedef for SearchEngine. Please update it whenever you add or remove
179 base::DictionaryValue
* dict
= new base::DictionaryValue();
180 dict
->SetString("name", template_url
->short_name());
181 dict
->SetString("displayName", table_model
->GetText(
182 index
, IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN
));
183 dict
->SetString("keyword", table_model
->GetText(
184 index
, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN
));
185 dict
->SetString("url", template_url
->url_ref().DisplayURL(
186 UIThreadSearchTermsData(Profile::FromWebUI(web_ui()))));
187 dict
->SetBoolean("urlLocked", template_url
->prepopulate_id() > 0);
188 GURL icon_url
= template_url
->favicon_url();
189 if (icon_url
.is_valid())
190 dict
->SetString("iconURL", icon_url
.spec());
191 dict
->SetString("modelIndex", base::IntToString(index
));
193 dict
->SetBoolean("canBeRemoved",
194 list_controller_
->CanRemove(template_url
));
195 dict
->SetBoolean("canBeDefault",
196 list_controller_
->CanMakeDefault(template_url
));
197 dict
->SetBoolean("default", is_default
);
198 dict
->SetBoolean("canBeEdited", list_controller_
->CanEdit(template_url
));
199 TemplateURL::Type type
= template_url
->GetType();
200 dict
->SetBoolean("isOmniboxExtension",
201 type
== TemplateURL::OMNIBOX_API_EXTENSION
);
202 if (type
== TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION
||
203 type
== TemplateURL::OMNIBOX_API_EXTENSION
) {
204 const extensions::Extension
* extension
=
205 extensions::ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
206 ->GetExtensionById(template_url
->GetExtensionId(),
207 extensions::ExtensionRegistry::EVERYTHING
);
209 dict
->Set("extension",
210 extensions::util::GetExtensionInfo(extension
).release());
216 void SearchEngineManagerHandler::SetDefaultSearchEngine(
217 const base::ListValue
* args
) {
219 if (!ExtractIntegerValue(args
, &index
)) {
223 if (index
< 0 || index
>= list_controller_
->table_model()->RowCount())
226 list_controller_
->MakeDefaultTemplateURL(index
);
228 content::RecordAction(
229 base::UserMetricsAction("Options_SearchEngineSetDefault"));
232 void SearchEngineManagerHandler::RemoveSearchEngine(
233 const base::ListValue
* args
) {
235 if (!ExtractIntegerValue(args
, &index
)) {
239 if (index
< 0 || index
>= list_controller_
->table_model()->RowCount())
242 if (list_controller_
->CanRemove(list_controller_
->GetTemplateURL(index
))) {
243 list_controller_
->RemoveTemplateURL(index
);
244 content::RecordAction(
245 base::UserMetricsAction("Options_SearchEngineRemoved"));
249 void SearchEngineManagerHandler::EditSearchEngine(const base::ListValue
* args
) {
251 if (!ExtractIntegerValue(args
, &index
)) {
256 // Allow -1, which means we are adding a new engine.
257 if (index
< -1 || index
>= list_controller_
->table_model()->RowCount())
260 edit_controller_
.reset(new EditSearchEngineController(
261 (index
== -1) ? NULL
: list_controller_
->GetTemplateURL(index
), this,
262 Profile::FromWebUI(web_ui())));
265 void SearchEngineManagerHandler::OnEditedKeyword(
266 TemplateURL
* template_url
,
267 const base::string16
& title
,
268 const base::string16
& keyword
,
269 const std::string
& url
) {
270 DCHECK(!url
.empty());
272 list_controller_
->ModifyTemplateURL(template_url
, title
, keyword
, url
);
274 list_controller_
->AddTemplateURL(title
, keyword
, url
);
275 edit_controller_
.reset();
278 void SearchEngineManagerHandler::CheckSearchEngineInfoValidity(
279 const base::ListValue
* args
) {
280 if (!edit_controller_
.get())
283 base::string16 keyword
;
285 std::string modelIndex
;
286 if (!args
->GetString(ENGINE_NAME
, &name
) ||
287 !args
->GetString(ENGINE_KEYWORD
, &keyword
) ||
288 !args
->GetString(ENGINE_URL
, &url
) ||
289 !args
->GetString(3, &modelIndex
)) {
294 base::DictionaryValue validity
;
295 validity
.SetBoolean("name", edit_controller_
->IsTitleValid(name
));
296 validity
.SetBoolean("keyword", edit_controller_
->IsKeywordValid(keyword
));
297 validity
.SetBoolean("url", edit_controller_
->IsURLValid(url
));
298 base::StringValue
indexValue(modelIndex
);
299 web_ui()->CallJavascriptFunction("SearchEngineManager.validityCheckCallback",
300 validity
, indexValue
);
303 void SearchEngineManagerHandler::EditCancelled(const base::ListValue
* args
) {
304 if (!edit_controller_
.get())
306 edit_controller_
->CleanUpCancelledAdd();
307 edit_controller_
.reset();
310 void SearchEngineManagerHandler::EditCompleted(const base::ListValue
* args
) {
311 if (!edit_controller_
.get())
314 base::string16 keyword
;
316 if (!args
->GetString(ENGINE_NAME
, &name
) ||
317 !args
->GetString(ENGINE_KEYWORD
, &keyword
) ||
318 !args
->GetString(ENGINE_URL
, &url
)) {
323 // Recheck validity. It's possible to get here with invalid input if e.g. the
324 // user calls the right JS functions directly from the web inspector.
325 if (edit_controller_
->IsTitleValid(name
) &&
326 edit_controller_
->IsKeywordValid(keyword
) &&
327 edit_controller_
->IsURLValid(url
))
328 edit_controller_
->AcceptAddOrEdit(name
, keyword
, url
);
331 } // namespace options