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_pref_value_map.h"
29 #include "extensions/browser/extension_pref_value_map_factory.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/common/extension.h"
33 #include "grit/chromium_strings.h"
34 #include "grit/generated_resources.h"
35 #include "grit/locale_settings.h"
36 #include "grit/theme_resources.h"
37 #include "ui/base/l10n/l10n_util.h"
40 using base::UserMetricsAction;
46 // Only allow changes to the metrics reporting checkbox if we were succesfully
47 // able to change the service.
48 bool AllowMetricsReportingChange(const base::Value* to_value) {
50 if (!to_value->GetAsBoolean(&enable)) {
55 return enable == OptionsUtil::ResolveMetricsReportingEnabled(enable);
60 CoreOptionsHandler::CoreOptionsHandler()
61 : handlers_host_(NULL) {
64 CoreOptionsHandler::~CoreOptionsHandler() {}
66 void CoreOptionsHandler::InitializeHandler() {
67 Profile* profile = Profile::FromWebUI(web_ui());
69 plugin_status_pref_setter_.Init(
71 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
72 base::Unretained(this),
73 profile->GetPrefs()));
75 pref_change_filters_[prefs::kMetricsReportingEnabled] =
76 base::Bind(&AllowMetricsReportingChange);
79 void CoreOptionsHandler::InitializePage() {
80 UpdateClearPluginLSOData();
81 UpdatePepperFlashSettingsEnabled();
84 void CoreOptionsHandler::GetLocalizedValues(
85 base::DictionaryValue* localized_strings) {
86 GetStaticLocalizedValues(localized_strings);
89 void CoreOptionsHandler::GetStaticLocalizedValues(
90 base::DictionaryValue* localized_strings) {
91 DCHECK(localized_strings);
93 localized_strings->SetString("optionsPageTitle",
94 l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
96 // Controlled settings bubble.
97 localized_strings->SetString("controlledSettingPolicy",
98 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
99 localized_strings->SetString("controlledSettingExtension",
100 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION));
101 localized_strings->SetString("controlledSettingExtensionWithName",
102 l10n_util::GetStringUTF16(
103 IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION_WITH_NAME));
104 localized_strings->SetString("controlledSettingManageExtension",
105 l10n_util::GetStringUTF16(
106 IDS_OPTIONS_CONTROLLED_SETTING_MANAGE_EXTENSION));
107 localized_strings->SetString("controlledSettingDisableExtension",
108 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE));
109 localized_strings->SetString("controlledSettingRecommended",
110 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_RECOMMENDED));
111 localized_strings->SetString("controlledSettingHasRecommendation",
112 l10n_util::GetStringUTF16(
113 IDS_OPTIONS_CONTROLLED_SETTING_HAS_RECOMMENDATION));
114 localized_strings->SetString("controlledSettingFollowRecommendation",
115 l10n_util::GetStringUTF16(
116 IDS_OPTIONS_CONTROLLED_SETTING_FOLLOW_RECOMMENDATION));
117 localized_strings->SetString("controlledSettingsPolicy",
118 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_POLICY));
119 localized_strings->SetString("controlledSettingsExtension",
120 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION));
121 localized_strings->SetString("controlledSettingsExtensionWithName",
122 l10n_util::GetStringUTF16(
123 IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION_WITH_NAME));
126 RegisterTitle(localized_strings, "searchPage", IDS_OPTIONS_SEARCH_PAGE_TITLE);
127 localized_strings->SetString("searchPlaceholder",
128 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PLACEHOLDER));
129 localized_strings->SetString("searchPageNoMatches",
130 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_NO_MATCHES));
131 localized_strings->SetString("searchPageHelpLabel",
132 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_LABEL));
133 localized_strings->SetString("searchPageHelpTitle",
134 l10n_util::GetStringFUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_TITLE,
135 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
136 localized_strings->SetString("searchPageHelpURL",
137 chrome::kSettingsSearchHelpURL);
140 localized_strings->SetString("ok",
141 l10n_util::GetStringUTF16(IDS_OK));
142 localized_strings->SetString("cancel",
143 l10n_util::GetStringUTF16(IDS_CANCEL));
144 localized_strings->SetString("learnMore",
145 l10n_util::GetStringUTF16(IDS_LEARN_MORE));
146 localized_strings->SetString("close",
147 l10n_util::GetStringUTF16(IDS_CLOSE));
148 localized_strings->SetString("done",
149 l10n_util::GetStringUTF16(IDS_DONE));
152 void CoreOptionsHandler::Uninitialize() {
153 std::string last_pref;
154 for (PreferenceCallbackMap::const_iterator iter = pref_callback_map_.begin();
155 iter != pref_callback_map_.end();
157 if (last_pref != iter->first) {
158 StopObservingPref(iter->first);
159 last_pref = iter->first;
164 void CoreOptionsHandler::OnPreferenceChanged(PrefService* service,
165 const std::string& pref_name) {
166 if (pref_name == prefs::kClearPluginLSODataEnabled) {
167 // This preference is stored in Local State, not in the user preferences.
168 UpdateClearPluginLSOData();
171 if (pref_name == prefs::kPepperFlashSettingsEnabled) {
172 UpdatePepperFlashSettingsEnabled();
175 NotifyPrefChanged(pref_name, std::string());
178 void CoreOptionsHandler::RegisterMessages() {
179 registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs());
180 local_state_registrar_.Init(g_browser_process->local_state());
182 web_ui()->RegisterMessageCallback("coreOptionsInitialize",
183 base::Bind(&CoreOptionsHandler::HandleInitialize,
184 base::Unretained(this)));
185 web_ui()->RegisterMessageCallback("onFinishedLoadingOptions",
186 base::Bind(&CoreOptionsHandler::OnFinishedLoading,
187 base::Unretained(this)));
188 web_ui()->RegisterMessageCallback("fetchPrefs",
189 base::Bind(&CoreOptionsHandler::HandleFetchPrefs,
190 base::Unretained(this)));
191 web_ui()->RegisterMessageCallback("observePrefs",
192 base::Bind(&CoreOptionsHandler::HandleObservePrefs,
193 base::Unretained(this)));
194 web_ui()->RegisterMessageCallback("setBooleanPref",
195 base::Bind(&CoreOptionsHandler::HandleSetBooleanPref,
196 base::Unretained(this)));
197 web_ui()->RegisterMessageCallback("setIntegerPref",
198 base::Bind(&CoreOptionsHandler::HandleSetIntegerPref,
199 base::Unretained(this)));
200 web_ui()->RegisterMessageCallback("setDoublePref",
201 base::Bind(&CoreOptionsHandler::HandleSetDoublePref,
202 base::Unretained(this)));
203 web_ui()->RegisterMessageCallback("setStringPref",
204 base::Bind(&CoreOptionsHandler::HandleSetStringPref,
205 base::Unretained(this)));
206 web_ui()->RegisterMessageCallback("setURLPref",
207 base::Bind(&CoreOptionsHandler::HandleSetURLPref,
208 base::Unretained(this)));
209 web_ui()->RegisterMessageCallback("setListPref",
210 base::Bind(&CoreOptionsHandler::HandleSetListPref,
211 base::Unretained(this)));
212 web_ui()->RegisterMessageCallback("clearPref",
213 base::Bind(&CoreOptionsHandler::HandleClearPref,
214 base::Unretained(this)));
215 web_ui()->RegisterMessageCallback("coreOptionsUserMetricsAction",
216 base::Bind(&CoreOptionsHandler::HandleUserMetricsAction,
217 base::Unretained(this)));
218 web_ui()->RegisterMessageCallback("disableExtension",
219 base::Bind(&CoreOptionsHandler::HandleDisableExtension,
220 base::Unretained(this)));
223 void CoreOptionsHandler::HandleInitialize(const base::ListValue* args) {
224 DCHECK(handlers_host_);
225 handlers_host_->InitializeHandlers();
228 void CoreOptionsHandler::OnFinishedLoading(const base::ListValue* args) {
229 DCHECK(handlers_host_);
230 handlers_host_->OnFinishedLoading();
233 base::Value* CoreOptionsHandler::FetchPref(const std::string& pref_name) {
234 return CreateValueForPref(pref_name, std::string());
237 void CoreOptionsHandler::ObservePref(const std::string& pref_name) {
238 if (g_browser_process->local_state()->FindPreference(pref_name.c_str())) {
239 local_state_registrar_.Add(
241 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
242 base::Unretained(this),
243 local_state_registrar_.prefs()));
245 // TODO(pneubeck): change this to if/else once kProxy is only used as a user
246 // pref. Currently, it is both a user and a local state pref.
247 if (Profile::FromWebUI(web_ui())->GetPrefs()->FindPreference(
248 pref_name.c_str())) {
251 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
252 base::Unretained(this),
253 registrar_.prefs()));
257 void CoreOptionsHandler::StopObservingPref(const std::string& pref_name) {
258 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
259 local_state_registrar_.Remove(pref_name.c_str());
261 registrar_.Remove(pref_name.c_str());
264 void CoreOptionsHandler::SetPref(const std::string& pref_name,
265 const base::Value* value,
266 const std::string& metric) {
267 PrefService* pref_service = FindServiceForPref(pref_name);
268 PrefChangeFilterMap::iterator iter = pref_change_filters_.find(pref_name);
269 if (iter != pref_change_filters_.end()) {
270 // Also check if the pref is user modifiable (don't even try to run the
271 // filter function if the user is not allowed to change the pref).
272 const PrefService::Preference* pref =
273 pref_service->FindPreference(pref_name.c_str());
274 if ((pref && !pref->IsUserModifiable()) || !iter->second.Run(value)) {
275 // Reject the change; remind the page of the true value.
276 NotifyPrefChanged(pref_name, std::string());
281 switch (value->GetType()) {
282 case base::Value::TYPE_BOOLEAN:
283 case base::Value::TYPE_INTEGER:
284 case base::Value::TYPE_DOUBLE:
285 case base::Value::TYPE_STRING:
286 case base::Value::TYPE_LIST:
287 pref_service->Set(pref_name.c_str(), *value);
295 ProcessUserMetric(value, metric);
298 void CoreOptionsHandler::ClearPref(const std::string& pref_name,
299 const std::string& metric) {
300 PrefService* pref_service = FindServiceForPref(pref_name);
301 pref_service->ClearPref(pref_name.c_str());
304 content::RecordComputedAction(metric);
307 void CoreOptionsHandler::ProcessUserMetric(const base::Value* value,
308 const std::string& metric) {
312 std::string metric_string = metric;
313 if (value->IsType(base::Value::TYPE_BOOLEAN)) {
315 CHECK(value->GetAsBoolean(&bool_value));
316 metric_string += bool_value ? "_Enable" : "_Disable";
319 content::RecordComputedAction(metric_string);
322 void CoreOptionsHandler::NotifyPrefChanged(
323 const std::string& pref_name,
324 const std::string& controlling_pref_name) {
325 scoped_ptr<base::Value> value(
326 CreateValueForPref(pref_name, controlling_pref_name));
327 DispatchPrefChangeNotification(pref_name, value.Pass());
330 void CoreOptionsHandler::DispatchPrefChangeNotification(
331 const std::string& name,
332 scoped_ptr<base::Value> value) {
333 std::pair<PreferenceCallbackMap::const_iterator,
334 PreferenceCallbackMap::const_iterator> range =
335 pref_callback_map_.equal_range(name);
336 base::ListValue result_value;
337 result_value.Append(new base::StringValue(name.c_str()));
338 result_value.Append(value.release());
339 for (PreferenceCallbackMap::const_iterator iter = range.first;
340 iter != range.second; ++iter) {
341 const std::string& callback_function = iter->second;
342 web_ui()->CallJavascriptFunction(callback_function, result_value);
346 base::Value* CoreOptionsHandler::CreateValueForPref(
347 const std::string& pref_name,
348 const std::string& controlling_pref_name) {
349 const PrefService* pref_service = FindServiceForPref(pref_name.c_str());
350 const PrefService::Preference* pref =
351 pref_service->FindPreference(pref_name.c_str());
354 return base::Value::CreateNullValue();
356 const PrefService::Preference* controlling_pref =
357 pref_service->FindPreference(controlling_pref_name.c_str());
358 if (!controlling_pref)
359 controlling_pref = pref;
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->IsExtensionControlled()) {
366 dict->SetString("controlledBy", "extension");
367 Profile* profile = Profile::FromWebUI(web_ui());
368 ExtensionPrefValueMap* extension_pref_value_map =
369 ExtensionPrefValueMapFactory::GetForBrowserContext(profile);
370 std::string extension_id =
371 extension_pref_value_map->GetExtensionControllingPref(
372 controlling_pref->name());
374 const extensions::Extension* extension =
375 extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
376 extension_id, extensions::ExtensionRegistry::EVERYTHING);
378 dict->Set("extension",
379 extensions::util::GetExtensionInfo(extension).release());
381 } else if (controlling_pref->IsRecommended()) {
382 dict->SetString("controlledBy", "recommended");
385 const base::Value* recommended_value =
386 controlling_pref->GetRecommendedValue();
387 if (recommended_value)
388 dict->Set("recommendedValue", recommended_value->DeepCopy());
389 dict->SetBoolean("disabled", !controlling_pref->IsUserModifiable());
393 PrefService* CoreOptionsHandler::FindServiceForPref(
394 const std::string& pref_name) {
395 // Proxy is a peculiar case: on ChromeOS, settings exist in both user
396 // prefs and local state, but chrome://settings should affect only user prefs.
397 // Elsewhere the proxy settings are stored in local state.
398 // See http://crbug.com/157147
399 PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
400 if (pref_name == prefs::kProxy)
401 #if defined(OS_CHROMEOS)
404 return g_browser_process->local_state();
407 // Find which PrefService contains the given pref. Pref names should not
408 // be duplicated across services, however if they are, prefer the user's
410 if (user_prefs->FindPreference(pref_name.c_str()))
413 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
414 return g_browser_process->local_state();
419 void CoreOptionsHandler::HandleFetchPrefs(const base::ListValue* args) {
420 // First param is name of callback function, so, there needs to be at least
421 // one more element for the actual preference identifier.
422 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
424 // Get callback JS function name.
425 const base::Value* callback;
426 if (!args->Get(0, &callback) || !callback->IsType(base::Value::TYPE_STRING))
429 base::string16 callback_function;
430 if (!callback->GetAsString(&callback_function))
433 // Get the list of name for prefs to build the response dictionary.
434 base::DictionaryValue result_value;
435 const base::Value* list_member;
437 for (size_t i = 1; i < args->GetSize(); i++) {
438 if (!args->Get(i, &list_member))
441 if (!list_member->IsType(base::Value::TYPE_STRING))
444 std::string pref_name;
445 if (!list_member->GetAsString(&pref_name))
448 result_value.Set(pref_name.c_str(), FetchPref(pref_name));
450 web_ui()->CallJavascriptFunction(base::UTF16ToASCII(callback_function),
454 void CoreOptionsHandler::HandleObservePrefs(const base::ListValue* args) {
455 // First param is name is JS callback function name, the rest are pref
456 // identifiers that we are observing.
457 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
459 // Get preference change callback function name.
460 std::string callback_func_name;
461 if (!args->GetString(0, &callback_func_name))
464 // Get all other parameters - pref identifiers.
465 for (size_t i = 1; i < args->GetSize(); i++) {
466 const base::Value* list_member;
467 if (!args->Get(i, &list_member))
470 // Just ignore bad pref identifiers for now.
471 std::string pref_name;
472 if (!list_member->IsType(base::Value::TYPE_STRING) ||
473 !list_member->GetAsString(&pref_name))
476 if (pref_callback_map_.find(pref_name) == pref_callback_map_.end())
477 ObservePref(pref_name);
479 pref_callback_map_.insert(
480 PreferenceCallbackMap::value_type(pref_name, callback_func_name));
484 void CoreOptionsHandler::HandleSetBooleanPref(const base::ListValue* args) {
485 HandleSetPref(args, TYPE_BOOLEAN);
488 void CoreOptionsHandler::HandleSetIntegerPref(const base::ListValue* args) {
489 HandleSetPref(args, TYPE_INTEGER);
492 void CoreOptionsHandler::HandleSetDoublePref(const base::ListValue* args) {
493 HandleSetPref(args, TYPE_DOUBLE);
496 void CoreOptionsHandler::HandleSetStringPref(const base::ListValue* args) {
497 HandleSetPref(args, TYPE_STRING);
500 void CoreOptionsHandler::HandleSetURLPref(const base::ListValue* args) {
501 HandleSetPref(args, TYPE_URL);
504 void CoreOptionsHandler::HandleSetListPref(const base::ListValue* args) {
505 HandleSetPref(args, TYPE_LIST);
508 void CoreOptionsHandler::HandleSetPref(const base::ListValue* args,
510 DCHECK_GT(static_cast<int>(args->GetSize()), 1);
512 std::string pref_name;
513 if (!args->GetString(0, &pref_name))
516 const base::Value* value;
517 if (!args->Get(1, &value))
520 scoped_ptr<base::Value> temp_value;
524 if (!value->IsType(base::Value::TYPE_BOOLEAN)) {
530 // In JS all numbers are doubles.
532 if (!value->GetAsDouble(&double_value)) {
536 int int_value = static_cast<int>(double_value);
537 temp_value.reset(new base::FundamentalValue(int_value));
538 value = temp_value.get();
542 if (!value->IsType(base::Value::TYPE_DOUBLE)) {
548 if (!value->IsType(base::Value::TYPE_STRING)) {
554 std::string original;
555 if (!value->GetAsString(&original)) {
559 GURL fixed = URLFixerUpper::FixupURL(original, std::string());
560 temp_value.reset(new base::StringValue(fixed.spec()));
561 value = temp_value.get();
565 // In case we have a List pref we got a JSON string.
566 std::string json_string;
567 if (!value->GetAsString(&json_string)) {
572 base::JSONReader::Read(json_string));
573 value = temp_value.get();
574 if (!value->IsType(base::Value::TYPE_LIST)) {
585 if (args->GetSize() > 2 && !args->GetString(2, &metric))
586 LOG(WARNING) << "Invalid metric parameter: " << pref_name;
587 SetPref(pref_name, value, metric);
590 void CoreOptionsHandler::HandleClearPref(const base::ListValue* args) {
591 DCHECK_GT(static_cast<int>(args->GetSize()), 0);
593 std::string pref_name;
594 if (!args->GetString(0, &pref_name))
598 if (args->GetSize() > 1) {
599 if (!args->GetString(1, &metric))
603 ClearPref(pref_name, metric);
606 void CoreOptionsHandler::HandleUserMetricsAction(const base::ListValue* args) {
607 std::string metric = base::UTF16ToUTF8(ExtractStringValue(args));
609 content::RecordComputedAction(metric);
612 void CoreOptionsHandler::HandleDisableExtension(const base::ListValue* args) {
613 std::string extension_id;
614 if (args->GetString(0, &extension_id)) {
615 ExtensionService* extension_service = extensions::ExtensionSystem::Get(
616 Profile::FromWebUI(web_ui()))->extension_service();
617 DCHECK(extension_service);
618 extension_service->DisableExtension(
619 extension_id, extensions::Extension::DISABLE_USER_ACTION);
625 void CoreOptionsHandler::UpdateClearPluginLSOData() {
626 base::FundamentalValue enabled(
627 plugin_status_pref_setter_.IsClearPluginLSODataEnabled());
628 web_ui()->CallJavascriptFunction(
629 "OptionsPage.setClearPluginLSODataEnabled", enabled);
632 void CoreOptionsHandler::UpdatePepperFlashSettingsEnabled() {
633 base::FundamentalValue enabled(
634 plugin_status_pref_setter_.IsPepperFlashSettingsEnabled());
635 web_ui()->CallJavascriptFunction(
636 "OptionsPage.setPepperFlashSettingsEnabled", enabled);
639 } // namespace options