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/core_options_handler.h"
8 #include "base/bind_helpers.h"
9 #include "base/json/json_reader.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_util.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/options/options_util.h"
21 #include "chrome/common/net/url_fixer_upper.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/common/url_constants.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/user_metrics.h"
27 #include "content/public/browser/web_ui.h"
28 #include "extensions/browser/extension_system.h"
29 #include "grit/chromium_strings.h"
30 #include "grit/generated_resources.h"
31 #include "grit/locale_settings.h"
32 #include "grit/theme_resources.h"
33 #include "ui/base/l10n/l10n_util.h"
36 using base::UserMetricsAction;
42 // Only allow changes to the metrics reporting checkbox if we were succesfully
43 // able to change the service.
44 bool AllowMetricsReportingChange(const base::Value* to_value) {
46 if (!to_value->GetAsBoolean(&enable)) {
51 return enable == OptionsUtil::ResolveMetricsReportingEnabled(enable);
56 CoreOptionsHandler::CoreOptionsHandler()
57 : handlers_host_(NULL) {
60 CoreOptionsHandler::~CoreOptionsHandler() {}
62 void CoreOptionsHandler::InitializeHandler() {
63 Profile* profile = Profile::FromWebUI(web_ui());
65 plugin_status_pref_setter_.Init(
67 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
68 base::Unretained(this),
69 profile->GetPrefs()));
71 pref_change_filters_[prefs::kMetricsReportingEnabled] =
72 base::Bind(&AllowMetricsReportingChange);
75 void CoreOptionsHandler::InitializePage() {
76 UpdateClearPluginLSOData();
77 UpdatePepperFlashSettingsEnabled();
80 void CoreOptionsHandler::GetLocalizedValues(
81 base::DictionaryValue* localized_strings) {
82 GetStaticLocalizedValues(localized_strings);
85 void CoreOptionsHandler::GetStaticLocalizedValues(
86 base::DictionaryValue* localized_strings) {
87 DCHECK(localized_strings);
89 localized_strings->SetString("optionsPageTitle",
90 l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
92 // Controlled settings bubble.
93 localized_strings->SetString("controlledSettingPolicy",
94 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
95 localized_strings->SetString("controlledSettingExtension",
96 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION));
97 localized_strings->SetString("controlledSettingExtensionWithName",
98 l10n_util::GetStringUTF16(
99 IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION_WITH_NAME));
100 localized_strings->SetString("controlledSettingManageExtension",
101 l10n_util::GetStringUTF16(
102 IDS_OPTIONS_CONTROLLED_SETTING_MANAGE_EXTENSION));
103 localized_strings->SetString("controlledSettingDisableExtension",
104 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE));
105 localized_strings->SetString("controlledSettingRecommended",
106 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_RECOMMENDED));
107 localized_strings->SetString("controlledSettingHasRecommendation",
108 l10n_util::GetStringUTF16(
109 IDS_OPTIONS_CONTROLLED_SETTING_HAS_RECOMMENDATION));
110 localized_strings->SetString("controlledSettingFollowRecommendation",
111 l10n_util::GetStringUTF16(
112 IDS_OPTIONS_CONTROLLED_SETTING_FOLLOW_RECOMMENDATION));
113 localized_strings->SetString("controlledSettingsPolicy",
114 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_POLICY));
115 localized_strings->SetString("controlledSettingsExtension",
116 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION));
117 localized_strings->SetString("controlledSettingsExtensionWithName",
118 l10n_util::GetStringUTF16(
119 IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION_WITH_NAME));
122 RegisterTitle(localized_strings, "searchPage", IDS_OPTIONS_SEARCH_PAGE_TITLE);
123 localized_strings->SetString("searchPlaceholder",
124 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PLACEHOLDER));
125 localized_strings->SetString("searchPageNoMatches",
126 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_NO_MATCHES));
127 localized_strings->SetString("searchPageHelpLabel",
128 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_LABEL));
129 localized_strings->SetString("searchPageHelpTitle",
130 l10n_util::GetStringFUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_TITLE,
131 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
132 localized_strings->SetString("searchPageHelpURL",
133 chrome::kSettingsSearchHelpURL);
136 localized_strings->SetString("ok",
137 l10n_util::GetStringUTF16(IDS_OK));
138 localized_strings->SetString("cancel",
139 l10n_util::GetStringUTF16(IDS_CANCEL));
140 localized_strings->SetString("learnMore",
141 l10n_util::GetStringUTF16(IDS_LEARN_MORE));
142 localized_strings->SetString("close",
143 l10n_util::GetStringUTF16(IDS_CLOSE));
144 localized_strings->SetString("done",
145 l10n_util::GetStringUTF16(IDS_DONE));
146 localized_strings->SetString("deletableItemDeleteButtonTitle",
147 l10n_util::GetStringUTF16(IDS_OPTIONS_DELETABLE_ITEM_DELETE_BUTTON));
150 void CoreOptionsHandler::Uninitialize() {
151 std::string last_pref;
152 for (PreferenceCallbackMap::const_iterator iter = pref_callback_map_.begin();
153 iter != pref_callback_map_.end();
155 if (last_pref != iter->first) {
156 StopObservingPref(iter->first);
157 last_pref = iter->first;
162 void CoreOptionsHandler::OnPreferenceChanged(PrefService* service,
163 const std::string& pref_name) {
164 if (pref_name == prefs::kClearPluginLSODataEnabled) {
165 // This preference is stored in Local State, not in the user preferences.
166 UpdateClearPluginLSOData();
169 if (pref_name == prefs::kPepperFlashSettingsEnabled) {
170 UpdatePepperFlashSettingsEnabled();
173 NotifyPrefChanged(pref_name, std::string());
176 void CoreOptionsHandler::RegisterMessages() {
177 registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs());
178 local_state_registrar_.Init(g_browser_process->local_state());
180 web_ui()->RegisterMessageCallback("coreOptionsInitialize",
181 base::Bind(&CoreOptionsHandler::HandleInitialize,
182 base::Unretained(this)));
183 web_ui()->RegisterMessageCallback("onFinishedLoadingOptions",
184 base::Bind(&CoreOptionsHandler::OnFinishedLoading,
185 base::Unretained(this)));
186 web_ui()->RegisterMessageCallback("fetchPrefs",
187 base::Bind(&CoreOptionsHandler::HandleFetchPrefs,
188 base::Unretained(this)));
189 web_ui()->RegisterMessageCallback("observePrefs",
190 base::Bind(&CoreOptionsHandler::HandleObservePrefs,
191 base::Unretained(this)));
192 web_ui()->RegisterMessageCallback("setBooleanPref",
193 base::Bind(&CoreOptionsHandler::HandleSetBooleanPref,
194 base::Unretained(this)));
195 web_ui()->RegisterMessageCallback("setIntegerPref",
196 base::Bind(&CoreOptionsHandler::HandleSetIntegerPref,
197 base::Unretained(this)));
198 web_ui()->RegisterMessageCallback("setDoublePref",
199 base::Bind(&CoreOptionsHandler::HandleSetDoublePref,
200 base::Unretained(this)));
201 web_ui()->RegisterMessageCallback("setStringPref",
202 base::Bind(&CoreOptionsHandler::HandleSetStringPref,
203 base::Unretained(this)));
204 web_ui()->RegisterMessageCallback("setURLPref",
205 base::Bind(&CoreOptionsHandler::HandleSetURLPref,
206 base::Unretained(this)));
207 web_ui()->RegisterMessageCallback("setListPref",
208 base::Bind(&CoreOptionsHandler::HandleSetListPref,
209 base::Unretained(this)));
210 web_ui()->RegisterMessageCallback("clearPref",
211 base::Bind(&CoreOptionsHandler::HandleClearPref,
212 base::Unretained(this)));
213 web_ui()->RegisterMessageCallback("coreOptionsUserMetricsAction",
214 base::Bind(&CoreOptionsHandler::HandleUserMetricsAction,
215 base::Unretained(this)));
216 web_ui()->RegisterMessageCallback("disableExtension",
217 base::Bind(&CoreOptionsHandler::HandleDisableExtension,
218 base::Unretained(this)));
221 void CoreOptionsHandler::HandleInitialize(const base::ListValue* args) {
222 DCHECK(handlers_host_);
223 handlers_host_->InitializeHandlers();
226 void CoreOptionsHandler::OnFinishedLoading(const base::ListValue* args) {
227 DCHECK(handlers_host_);
228 handlers_host_->OnFinishedLoading();
231 base::Value* CoreOptionsHandler::FetchPref(const std::string& pref_name) {
232 return CreateValueForPref(pref_name, std::string());
235 void CoreOptionsHandler::ObservePref(const std::string& pref_name) {
236 if (g_browser_process->local_state()->FindPreference(pref_name.c_str())) {
237 local_state_registrar_.Add(
239 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
240 base::Unretained(this),
241 local_state_registrar_.prefs()));
243 // TODO(pneubeck): change this to if/else once kProxy is only used as a user
244 // pref. Currently, it is both a user and a local state pref.
245 if (Profile::FromWebUI(web_ui())->GetPrefs()->FindPreference(
246 pref_name.c_str())) {
249 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
250 base::Unretained(this),
251 registrar_.prefs()));
255 void CoreOptionsHandler::StopObservingPref(const std::string& pref_name) {
256 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
257 local_state_registrar_.Remove(pref_name.c_str());
259 registrar_.Remove(pref_name.c_str());
262 void CoreOptionsHandler::SetPref(const std::string& pref_name,
263 const base::Value* value,
264 const std::string& metric) {
265 PrefService* pref_service = FindServiceForPref(pref_name);
266 PrefChangeFilterMap::iterator iter = pref_change_filters_.find(pref_name);
267 if (iter != pref_change_filters_.end()) {
268 // Also check if the pref is user modifiable (don't even try to run the
269 // filter function if the user is not allowed to change the pref).
270 const PrefService::Preference* pref =
271 pref_service->FindPreference(pref_name.c_str());
272 if ((pref && !pref->IsUserModifiable()) || !iter->second.Run(value)) {
273 // Reject the change; remind the page of the true value.
274 NotifyPrefChanged(pref_name, std::string());
279 switch (value->GetType()) {
280 case base::Value::TYPE_BOOLEAN:
281 case base::Value::TYPE_INTEGER:
282 case base::Value::TYPE_DOUBLE:
283 case base::Value::TYPE_STRING:
284 case base::Value::TYPE_LIST:
285 pref_service->Set(pref_name.c_str(), *value);
293 ProcessUserMetric(value, metric);
296 void CoreOptionsHandler::ClearPref(const std::string& pref_name,
297 const std::string& metric) {
298 PrefService* pref_service = FindServiceForPref(pref_name);
299 pref_service->ClearPref(pref_name.c_str());
302 content::RecordComputedAction(metric);
305 void CoreOptionsHandler::ProcessUserMetric(const base::Value* value,
306 const std::string& metric) {
310 std::string metric_string = metric;
311 if (value->IsType(base::Value::TYPE_BOOLEAN)) {
313 CHECK(value->GetAsBoolean(&bool_value));
314 metric_string += bool_value ? "_Enable" : "_Disable";
317 content::RecordComputedAction(metric_string);
320 void CoreOptionsHandler::NotifyPrefChanged(
321 const std::string& pref_name,
322 const std::string& controlling_pref_name) {
323 scoped_ptr<base::Value> value(
324 CreateValueForPref(pref_name, controlling_pref_name));
325 DispatchPrefChangeNotification(pref_name, value.Pass());
328 void CoreOptionsHandler::DispatchPrefChangeNotification(
329 const std::string& name,
330 scoped_ptr<base::Value> value) {
331 std::pair<PreferenceCallbackMap::const_iterator,
332 PreferenceCallbackMap::const_iterator> range =
333 pref_callback_map_.equal_range(name);
334 base::ListValue result_value;
335 result_value.Append(new base::StringValue(name.c_str()));
336 result_value.Append(value.release());
337 for (PreferenceCallbackMap::const_iterator iter = range.first;
338 iter != range.second; ++iter) {
339 const std::string& callback_function = iter->second;
340 web_ui()->CallJavascriptFunction(callback_function, result_value);
344 base::Value* CoreOptionsHandler::CreateValueForPref(
345 const std::string& pref_name,
346 const std::string& controlling_pref_name) {
347 const PrefService* pref_service = FindServiceForPref(pref_name.c_str());
348 const PrefService::Preference* pref =
349 pref_service->FindPreference(pref_name.c_str());
352 return base::Value::CreateNullValue();
354 const PrefService::Preference* controlling_pref =
355 pref_service->FindPreference(controlling_pref_name.c_str());
356 if (!controlling_pref)
357 controlling_pref = pref;
359 // We don't show a UI here for extension controlled values because we opted to
360 // show a more obvious UI than an extension puzzle piece on the settings page.
361 base::DictionaryValue* dict = new base::DictionaryValue;
362 dict->Set("value", pref->GetValue()->DeepCopy());
363 if (controlling_pref->IsManaged())
364 dict->SetString("controlledBy", "policy");
365 else if (controlling_pref->IsRecommended())
366 dict->SetString("controlledBy", "recommended");
368 const base::Value* recommended_value =
369 controlling_pref->GetRecommendedValue();
370 if (recommended_value)
371 dict->Set("recommendedValue", recommended_value->DeepCopy());
372 dict->SetBoolean("disabled", !controlling_pref->IsUserModifiable());
376 PrefService* CoreOptionsHandler::FindServiceForPref(
377 const std::string& pref_name) {
378 // Proxy is a peculiar case: on ChromeOS, settings exist in both user
379 // prefs and local state, but chrome://settings should affect only user prefs.
380 // Elsewhere the proxy settings are stored in local state.
381 // See http://crbug.com/157147
382 PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
383 if (pref_name == prefs::kProxy)
384 #if defined(OS_CHROMEOS)
387 return g_browser_process->local_state();
390 // Find which PrefService contains the given pref. Pref names should not
391 // be duplicated across services, however if they are, prefer the user's
393 if (user_prefs->FindPreference(pref_name.c_str()))
396 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
397 return g_browser_process->local_state();
402 void CoreOptionsHandler::HandleFetchPrefs(const base::ListValue* args) {
403 // First param is name of callback function, so, there needs to be at least
404 // one more element for the actual preference identifier.
405 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
407 // Get callback JS function name.
408 const base::Value* callback;
409 if (!args->Get(0, &callback) || !callback->IsType(base::Value::TYPE_STRING))
412 base::string16 callback_function;
413 if (!callback->GetAsString(&callback_function))
416 // Get the list of name for prefs to build the response dictionary.
417 base::DictionaryValue result_value;
418 const base::Value* list_member;
420 for (size_t i = 1; i < args->GetSize(); i++) {
421 if (!args->Get(i, &list_member))
424 if (!list_member->IsType(base::Value::TYPE_STRING))
427 std::string pref_name;
428 if (!list_member->GetAsString(&pref_name))
431 result_value.Set(pref_name.c_str(), FetchPref(pref_name));
433 web_ui()->CallJavascriptFunction(base::UTF16ToASCII(callback_function),
437 void CoreOptionsHandler::HandleObservePrefs(const base::ListValue* args) {
438 // First param is name is JS callback function name, the rest are pref
439 // identifiers that we are observing.
440 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
442 // Get preference change callback function name.
443 std::string callback_func_name;
444 if (!args->GetString(0, &callback_func_name))
447 // Get all other parameters - pref identifiers.
448 for (size_t i = 1; i < args->GetSize(); i++) {
449 const base::Value* list_member;
450 if (!args->Get(i, &list_member))
453 // Just ignore bad pref identifiers for now.
454 std::string pref_name;
455 if (!list_member->IsType(base::Value::TYPE_STRING) ||
456 !list_member->GetAsString(&pref_name))
459 if (pref_callback_map_.find(pref_name) == pref_callback_map_.end())
460 ObservePref(pref_name);
462 pref_callback_map_.insert(
463 PreferenceCallbackMap::value_type(pref_name, callback_func_name));
467 void CoreOptionsHandler::HandleSetBooleanPref(const base::ListValue* args) {
468 HandleSetPref(args, TYPE_BOOLEAN);
471 void CoreOptionsHandler::HandleSetIntegerPref(const base::ListValue* args) {
472 HandleSetPref(args, TYPE_INTEGER);
475 void CoreOptionsHandler::HandleSetDoublePref(const base::ListValue* args) {
476 HandleSetPref(args, TYPE_DOUBLE);
479 void CoreOptionsHandler::HandleSetStringPref(const base::ListValue* args) {
480 HandleSetPref(args, TYPE_STRING);
483 void CoreOptionsHandler::HandleSetURLPref(const base::ListValue* args) {
484 HandleSetPref(args, TYPE_URL);
487 void CoreOptionsHandler::HandleSetListPref(const base::ListValue* args) {
488 HandleSetPref(args, TYPE_LIST);
491 void CoreOptionsHandler::HandleSetPref(const base::ListValue* args,
493 DCHECK_GT(static_cast<int>(args->GetSize()), 1);
495 std::string pref_name;
496 if (!args->GetString(0, &pref_name))
499 const base::Value* value;
500 if (!args->Get(1, &value))
503 scoped_ptr<base::Value> temp_value;
507 if (!value->IsType(base::Value::TYPE_BOOLEAN)) {
513 // In JS all numbers are doubles.
515 if (!value->GetAsDouble(&double_value)) {
519 int int_value = static_cast<int>(double_value);
520 temp_value.reset(new base::FundamentalValue(int_value));
521 value = temp_value.get();
525 if (!value->IsType(base::Value::TYPE_DOUBLE)) {
531 if (!value->IsType(base::Value::TYPE_STRING)) {
537 std::string original;
538 if (!value->GetAsString(&original)) {
542 GURL fixed = URLFixerUpper::FixupURL(original, std::string());
543 temp_value.reset(new base::StringValue(fixed.spec()));
544 value = temp_value.get();
548 // In case we have a List pref we got a JSON string.
549 std::string json_string;
550 if (!value->GetAsString(&json_string)) {
555 base::JSONReader::Read(json_string));
556 value = temp_value.get();
557 if (!value->IsType(base::Value::TYPE_LIST)) {
568 if (args->GetSize() > 2 && !args->GetString(2, &metric))
569 LOG(WARNING) << "Invalid metric parameter: " << pref_name;
570 SetPref(pref_name, value, metric);
573 void CoreOptionsHandler::HandleClearPref(const base::ListValue* args) {
574 DCHECK_GT(static_cast<int>(args->GetSize()), 0);
576 std::string pref_name;
577 if (!args->GetString(0, &pref_name))
581 if (args->GetSize() > 1) {
582 if (!args->GetString(1, &metric))
586 ClearPref(pref_name, metric);
589 void CoreOptionsHandler::HandleUserMetricsAction(const base::ListValue* args) {
590 std::string metric = base::UTF16ToUTF8(ExtractStringValue(args));
592 content::RecordComputedAction(metric);
595 void CoreOptionsHandler::HandleDisableExtension(const base::ListValue* args) {
596 std::string extension_id;
597 if (args->GetString(0, &extension_id)) {
598 ExtensionService* extension_service = extensions::ExtensionSystem::Get(
599 Profile::FromWebUI(web_ui()))->extension_service();
600 DCHECK(extension_service);
601 extension_service->DisableExtension(
602 extension_id, extensions::Extension::DISABLE_USER_ACTION);
608 void CoreOptionsHandler::UpdateClearPluginLSOData() {
609 base::FundamentalValue enabled(
610 plugin_status_pref_setter_.IsClearPluginLSODataEnabled());
611 web_ui()->CallJavascriptFunction(
612 "OptionsPage.setClearPluginLSODataEnabled", enabled);
615 void CoreOptionsHandler::UpdatePepperFlashSettingsEnabled() {
616 base::FundamentalValue enabled(
617 plugin_status_pref_setter_.IsPepperFlashSettingsEnabled());
618 web_ui()->CallJavascriptFunction(
619 "OptionsPage.setPepperFlashSettingsEnabled", enabled);
622 } // namespace options