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/profiles/profile.h"
18 #include "chrome/browser/ui/options/options_util.h"
19 #include "chrome/common/net/url_fixer_upper.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/common/url_constants.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/user_metrics.h"
25 #include "content/public/browser/web_ui.h"
26 #include "grit/chromium_strings.h"
27 #include "grit/generated_resources.h"
28 #include "grit/locale_settings.h"
29 #include "grit/theme_resources.h"
30 #include "ui/base/l10n/l10n_util.h"
33 using content::UserMetricsAction;
39 // Only allow changes to the metrics reporting checkbox if we were succesfully
40 // able to change the service.
41 bool AllowMetricsReportingChange(const base::Value* to_value) {
43 if (!to_value->GetAsBoolean(&enable)) {
48 return enable == OptionsUtil::ResolveMetricsReportingEnabled(enable);
53 CoreOptionsHandler::CoreOptionsHandler()
54 : handlers_host_(NULL) {
57 CoreOptionsHandler::~CoreOptionsHandler() {}
59 void CoreOptionsHandler::InitializeHandler() {
60 Profile* profile = Profile::FromWebUI(web_ui());
62 plugin_status_pref_setter_.Init(
64 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
65 base::Unretained(this),
66 profile->GetPrefs()));
68 pref_change_filters_[prefs::kMetricsReportingEnabled] =
69 base::Bind(&AllowMetricsReportingChange);
72 void CoreOptionsHandler::InitializePage() {
73 UpdateClearPluginLSOData();
74 UpdatePepperFlashSettingsEnabled();
77 void CoreOptionsHandler::GetLocalizedValues(
78 DictionaryValue* localized_strings) {
79 GetStaticLocalizedValues(localized_strings);
82 void CoreOptionsHandler::GetStaticLocalizedValues(
83 base::DictionaryValue* localized_strings) {
84 DCHECK(localized_strings);
86 localized_strings->SetString("optionsPageTitle",
87 l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
89 // Controlled settings bubble.
90 localized_strings->SetString("controlledSettingPolicy",
91 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
92 localized_strings->SetString("controlledSettingExtension",
93 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION));
94 localized_strings->SetString("controlledSettingRecommended",
95 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_RECOMMENDED));
96 localized_strings->SetString("controlledSettingHasRecommendation",
97 l10n_util::GetStringUTF16(
98 IDS_OPTIONS_CONTROLLED_SETTING_HAS_RECOMMENDATION));
99 localized_strings->SetString("controlledSettingFollowRecommendation",
100 l10n_util::GetStringUTF16(
101 IDS_OPTIONS_CONTROLLED_SETTING_FOLLOW_RECOMMENDATION));
102 localized_strings->SetString("controlledSettingsPolicy",
103 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_POLICY));
104 localized_strings->SetString("controlledSettingsExtension",
105 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION));
108 RegisterTitle(localized_strings, "searchPage", IDS_OPTIONS_SEARCH_PAGE_TITLE);
109 localized_strings->SetString("searchPlaceholder",
110 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PLACEHOLDER));
111 localized_strings->SetString("searchPageNoMatches",
112 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_NO_MATCHES));
113 localized_strings->SetString("searchPageHelpLabel",
114 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_LABEL));
115 localized_strings->SetString("searchPageHelpTitle",
116 l10n_util::GetStringFUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_TITLE,
117 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
118 localized_strings->SetString("searchPageHelpURL",
119 chrome::kSettingsSearchHelpURL);
122 localized_strings->SetString("ok",
123 l10n_util::GetStringUTF16(IDS_OK));
124 localized_strings->SetString("cancel",
125 l10n_util::GetStringUTF16(IDS_CANCEL));
126 localized_strings->SetString("learnMore",
127 l10n_util::GetStringUTF16(IDS_LEARN_MORE));
128 localized_strings->SetString("close",
129 l10n_util::GetStringUTF16(IDS_CLOSE));
130 localized_strings->SetString("done",
131 l10n_util::GetStringUTF16(IDS_DONE));
134 void CoreOptionsHandler::Uninitialize() {
135 std::string last_pref;
136 for (PreferenceCallbackMap::const_iterator iter = pref_callback_map_.begin();
137 iter != pref_callback_map_.end();
139 if (last_pref != iter->first) {
140 StopObservingPref(iter->first);
141 last_pref = iter->first;
146 void CoreOptionsHandler::OnPreferenceChanged(PrefService* service,
147 const std::string& pref_name) {
148 if (pref_name == prefs::kClearPluginLSODataEnabled) {
149 // This preference is stored in Local State, not in the user preferences.
150 UpdateClearPluginLSOData();
153 if (pref_name == prefs::kPepperFlashSettingsEnabled) {
154 UpdatePepperFlashSettingsEnabled();
157 NotifyPrefChanged(pref_name, std::string());
160 void CoreOptionsHandler::RegisterMessages() {
161 registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs());
162 local_state_registrar_.Init(g_browser_process->local_state());
164 web_ui()->RegisterMessageCallback("coreOptionsInitialize",
165 base::Bind(&CoreOptionsHandler::HandleInitialize,
166 base::Unretained(this)));
167 web_ui()->RegisterMessageCallback("fetchPrefs",
168 base::Bind(&CoreOptionsHandler::HandleFetchPrefs,
169 base::Unretained(this)));
170 web_ui()->RegisterMessageCallback("observePrefs",
171 base::Bind(&CoreOptionsHandler::HandleObservePrefs,
172 base::Unretained(this)));
173 web_ui()->RegisterMessageCallback("setBooleanPref",
174 base::Bind(&CoreOptionsHandler::HandleSetBooleanPref,
175 base::Unretained(this)));
176 web_ui()->RegisterMessageCallback("setIntegerPref",
177 base::Bind(&CoreOptionsHandler::HandleSetIntegerPref,
178 base::Unretained(this)));
179 web_ui()->RegisterMessageCallback("setDoublePref",
180 base::Bind(&CoreOptionsHandler::HandleSetDoublePref,
181 base::Unretained(this)));
182 web_ui()->RegisterMessageCallback("setStringPref",
183 base::Bind(&CoreOptionsHandler::HandleSetStringPref,
184 base::Unretained(this)));
185 web_ui()->RegisterMessageCallback("setURLPref",
186 base::Bind(&CoreOptionsHandler::HandleSetURLPref,
187 base::Unretained(this)));
188 web_ui()->RegisterMessageCallback("setListPref",
189 base::Bind(&CoreOptionsHandler::HandleSetListPref,
190 base::Unretained(this)));
191 web_ui()->RegisterMessageCallback("clearPref",
192 base::Bind(&CoreOptionsHandler::HandleClearPref,
193 base::Unretained(this)));
194 web_ui()->RegisterMessageCallback("coreOptionsUserMetricsAction",
195 base::Bind(&CoreOptionsHandler::HandleUserMetricsAction,
196 base::Unretained(this)));
199 void CoreOptionsHandler::HandleInitialize(const ListValue* args) {
200 DCHECK(handlers_host_);
201 handlers_host_->InitializeHandlers();
204 base::Value* CoreOptionsHandler::FetchPref(const std::string& pref_name) {
205 return CreateValueForPref(pref_name, std::string());
208 void CoreOptionsHandler::ObservePref(const std::string& pref_name) {
209 if (g_browser_process->local_state()->FindPreference(pref_name.c_str())) {
210 local_state_registrar_.Add(
212 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
213 base::Unretained(this),
214 local_state_registrar_.prefs()));
216 // TODO(pneubeck): change this to if/else once kProxy is only used as a user
217 // pref. Currently, it is both a user and a local state pref.
218 if (Profile::FromWebUI(web_ui())->GetPrefs()->FindPreference(
219 pref_name.c_str())) {
222 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
223 base::Unretained(this),
224 registrar_.prefs()));
228 void CoreOptionsHandler::StopObservingPref(const std::string& pref_name) {
229 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
230 local_state_registrar_.Remove(pref_name.c_str());
232 registrar_.Remove(pref_name.c_str());
235 void CoreOptionsHandler::SetPref(const std::string& pref_name,
236 const base::Value* value,
237 const std::string& metric) {
238 PrefService* pref_service = FindServiceForPref(pref_name);
239 PrefChangeFilterMap::iterator iter = pref_change_filters_.find(pref_name);
240 if (iter != pref_change_filters_.end()) {
241 // Also check if the pref is user modifiable (don't even try to run the
242 // filter function if the user is not allowed to change the pref).
243 const PrefService::Preference* pref =
244 pref_service->FindPreference(pref_name.c_str());
245 if ((pref && !pref->IsUserModifiable()) || !iter->second.Run(value)) {
246 // Reject the change; remind the page of the true value.
247 NotifyPrefChanged(pref_name, std::string());
252 switch (value->GetType()) {
253 case base::Value::TYPE_BOOLEAN:
254 case base::Value::TYPE_INTEGER:
255 case base::Value::TYPE_DOUBLE:
256 case base::Value::TYPE_STRING:
257 case base::Value::TYPE_LIST:
258 pref_service->Set(pref_name.c_str(), *value);
266 ProcessUserMetric(value, metric);
269 void CoreOptionsHandler::ClearPref(const std::string& pref_name,
270 const std::string& metric) {
271 PrefService* pref_service = FindServiceForPref(pref_name);
272 pref_service->ClearPref(pref_name.c_str());
275 content::RecordComputedAction(metric);
278 void CoreOptionsHandler::ProcessUserMetric(const base::Value* value,
279 const std::string& metric) {
283 std::string metric_string = metric;
284 if (value->IsType(base::Value::TYPE_BOOLEAN)) {
286 CHECK(value->GetAsBoolean(&bool_value));
287 metric_string += bool_value ? "_Enable" : "_Disable";
290 content::RecordComputedAction(metric_string);
293 void CoreOptionsHandler::NotifyPrefChanged(
294 const std::string& pref_name,
295 const std::string& controlling_pref_name) {
296 scoped_ptr<base::Value> value(
297 CreateValueForPref(pref_name, controlling_pref_name));
298 DispatchPrefChangeNotification(pref_name, value.Pass());
301 void CoreOptionsHandler::DispatchPrefChangeNotification(
302 const std::string& name,
303 scoped_ptr<base::Value> value) {
304 std::pair<PreferenceCallbackMap::const_iterator,
305 PreferenceCallbackMap::const_iterator> range =
306 pref_callback_map_.equal_range(name);
307 ListValue result_value;
308 result_value.Append(new base::StringValue(name.c_str()));
309 result_value.Append(value.release());
310 for (PreferenceCallbackMap::const_iterator iter = range.first;
311 iter != range.second; ++iter) {
312 const std::string& callback_function = iter->second;
313 web_ui()->CallJavascriptFunction(callback_function, result_value);
317 base::Value* CoreOptionsHandler::CreateValueForPref(
318 const std::string& pref_name,
319 const std::string& controlling_pref_name) {
320 const PrefService* pref_service = FindServiceForPref(pref_name.c_str());
321 const PrefService::Preference* pref =
322 pref_service->FindPreference(pref_name.c_str());
325 return base::Value::CreateNullValue();
327 const PrefService::Preference* controlling_pref =
328 pref_service->FindPreference(controlling_pref_name.c_str());
329 if (!controlling_pref)
330 controlling_pref = pref;
332 DictionaryValue* dict = new DictionaryValue;
333 dict->Set("value", pref->GetValue()->DeepCopy());
334 if (controlling_pref->IsManaged())
335 dict->SetString("controlledBy", "policy");
336 else if (controlling_pref->IsExtensionControlled())
337 dict->SetString("controlledBy", "extension");
338 else if (controlling_pref->IsRecommended())
339 dict->SetString("controlledBy", "recommended");
341 const base::Value* recommended_value =
342 controlling_pref->GetRecommendedValue();
343 if (recommended_value)
344 dict->Set("recommendedValue", recommended_value->DeepCopy());
345 dict->SetBoolean("disabled", !controlling_pref->IsUserModifiable());
349 PrefService* CoreOptionsHandler::FindServiceForPref(
350 const std::string& pref_name) {
351 // Proxy is a peculiar case: on ChromeOS, settings exist in both user
352 // prefs and local state, but chrome://settings should affect only user prefs.
353 // Elsewhere the proxy settings are stored in local state.
354 // See http://crbug.com/157147
355 PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
356 if (pref_name == prefs::kProxy)
357 #if defined(OS_CHROMEOS)
360 return g_browser_process->local_state();
363 // Find which PrefService contains the given pref. Pref names should not
364 // be duplicated across services, however if they are, prefer the user's
366 if (user_prefs->FindPreference(pref_name.c_str()))
369 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
370 return g_browser_process->local_state();
375 void CoreOptionsHandler::HandleFetchPrefs(const ListValue* args) {
376 // First param is name of callback function, so, there needs to be at least
377 // one more element for the actual preference identifier.
378 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
380 // Get callback JS function name.
381 const base::Value* callback;
382 if (!args->Get(0, &callback) || !callback->IsType(base::Value::TYPE_STRING))
385 string16 callback_function;
386 if (!callback->GetAsString(&callback_function))
389 // Get the list of name for prefs to build the response dictionary.
390 DictionaryValue result_value;
391 const base::Value* list_member;
393 for (size_t i = 1; i < args->GetSize(); i++) {
394 if (!args->Get(i, &list_member))
397 if (!list_member->IsType(base::Value::TYPE_STRING))
400 std::string pref_name;
401 if (!list_member->GetAsString(&pref_name))
404 result_value.Set(pref_name.c_str(), FetchPref(pref_name));
406 web_ui()->CallJavascriptFunction(UTF16ToASCII(callback_function),
410 void CoreOptionsHandler::HandleObservePrefs(const ListValue* args) {
411 // First param is name is JS callback function name, the rest are pref
412 // identifiers that we are observing.
413 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
415 // Get preference change callback function name.
416 std::string callback_func_name;
417 if (!args->GetString(0, &callback_func_name))
420 // Get all other parameters - pref identifiers.
421 for (size_t i = 1; i < args->GetSize(); i++) {
422 const base::Value* list_member;
423 if (!args->Get(i, &list_member))
426 // Just ignore bad pref identifiers for now.
427 std::string pref_name;
428 if (!list_member->IsType(base::Value::TYPE_STRING) ||
429 !list_member->GetAsString(&pref_name))
432 if (pref_callback_map_.find(pref_name) == pref_callback_map_.end())
433 ObservePref(pref_name);
435 pref_callback_map_.insert(
436 PreferenceCallbackMap::value_type(pref_name, callback_func_name));
440 void CoreOptionsHandler::HandleSetBooleanPref(const ListValue* args) {
441 HandleSetPref(args, TYPE_BOOLEAN);
444 void CoreOptionsHandler::HandleSetIntegerPref(const ListValue* args) {
445 HandleSetPref(args, TYPE_INTEGER);
448 void CoreOptionsHandler::HandleSetDoublePref(const ListValue* args) {
449 HandleSetPref(args, TYPE_DOUBLE);
452 void CoreOptionsHandler::HandleSetStringPref(const ListValue* args) {
453 HandleSetPref(args, TYPE_STRING);
456 void CoreOptionsHandler::HandleSetURLPref(const ListValue* args) {
457 HandleSetPref(args, TYPE_URL);
460 void CoreOptionsHandler::HandleSetListPref(const ListValue* args) {
461 HandleSetPref(args, TYPE_LIST);
464 void CoreOptionsHandler::HandleSetPref(const ListValue* args, PrefType type) {
465 DCHECK_GT(static_cast<int>(args->GetSize()), 1);
467 std::string pref_name;
468 if (!args->GetString(0, &pref_name))
471 const base::Value* value;
472 if (!args->Get(1, &value))
475 scoped_ptr<base::Value> temp_value;
479 if (!value->IsType(base::Value::TYPE_BOOLEAN)) {
485 // In JS all numbers are doubles.
487 if (!value->GetAsDouble(&double_value)) {
491 int int_value = static_cast<int>(double_value);
492 temp_value.reset(new base::FundamentalValue(int_value));
493 value = temp_value.get();
497 if (!value->IsType(base::Value::TYPE_DOUBLE)) {
503 if (!value->IsType(base::Value::TYPE_STRING)) {
509 std::string original;
510 if (!value->GetAsString(&original)) {
514 GURL fixed = URLFixerUpper::FixupURL(original, std::string());
515 temp_value.reset(new base::StringValue(fixed.spec()));
516 value = temp_value.get();
520 // In case we have a List pref we got a JSON string.
521 std::string json_string;
522 if (!value->GetAsString(&json_string)) {
527 base::JSONReader::Read(json_string));
528 value = temp_value.get();
529 if (!value->IsType(base::Value::TYPE_LIST)) {
540 if (args->GetSize() > 2 && !args->GetString(2, &metric))
541 LOG(WARNING) << "Invalid metric parameter: " << pref_name;
542 SetPref(pref_name, value, metric);
545 void CoreOptionsHandler::HandleClearPref(const ListValue* args) {
546 DCHECK_GT(static_cast<int>(args->GetSize()), 0);
548 std::string pref_name;
549 if (!args->GetString(0, &pref_name))
553 if (args->GetSize() > 1) {
554 if (!args->GetString(1, &metric))
558 ClearPref(pref_name, metric);
561 void CoreOptionsHandler::HandleUserMetricsAction(const ListValue* args) {
562 std::string metric = UTF16ToUTF8(ExtractStringValue(args));
564 content::RecordComputedAction(metric);
567 void CoreOptionsHandler::UpdateClearPluginLSOData() {
568 base::FundamentalValue enabled(
569 plugin_status_pref_setter_.IsClearPluginLSODataEnabled());
570 web_ui()->CallJavascriptFunction(
571 "OptionsPage.setClearPluginLSODataEnabled", enabled);
574 void CoreOptionsHandler::UpdatePepperFlashSettingsEnabled() {
575 base::FundamentalValue enabled(
576 plugin_status_pref_setter_.IsPepperFlashSettingsEnabled());
577 web_ui()->CallJavascriptFunction(
578 "OptionsPage.setPepperFlashSettingsEnabled", enabled);
581 } // namespace options