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/metrics/metrics_reporting_state.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/common/url_constants.h"
23 #include "components/url_fixer/url_fixer.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 == ResolveMetricsReportingEnabled(enable);
58 // Whether "controlledBy" property of pref value sent to options web UI needs to
59 // be set to "extension" when the preference is controlled by an extension.
60 bool CanSetExtensionControlledPrefValue(
61 const PrefService::Preference* preference) {
63 // These have more obvious UI than the standard one for extension controlled
64 // values (an extension puzzle piece) on the settings page. To avoiding
65 // showing the extension puzzle piece for these settings, their "controlledBy"
66 // value should never be set to "extension".
67 return preference->name() != prefs::kURLsToRestoreOnStartup &&
68 preference->name() != prefs::kRestoreOnStartup &&
69 preference->name() != prefs::kHomePage &&
70 preference->name() != prefs::kHomePageIsNewTabPage;
78 CoreOptionsHandler::CoreOptionsHandler()
79 : handlers_host_(NULL) {
82 CoreOptionsHandler::~CoreOptionsHandler() {}
84 void CoreOptionsHandler::InitializeHandler() {
85 Profile* profile = Profile::FromWebUI(web_ui());
87 plugin_status_pref_setter_.Init(
89 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
90 base::Unretained(this),
91 profile->GetPrefs()));
93 pref_change_filters_[prefs::kMetricsReportingEnabled] =
94 base::Bind(&AllowMetricsReportingChange);
97 void CoreOptionsHandler::InitializePage() {
98 UpdateClearPluginLSOData();
99 UpdatePepperFlashSettingsEnabled();
102 void CoreOptionsHandler::GetLocalizedValues(
103 base::DictionaryValue* localized_strings) {
104 GetStaticLocalizedValues(localized_strings);
107 void CoreOptionsHandler::GetStaticLocalizedValues(
108 base::DictionaryValue* localized_strings) {
109 DCHECK(localized_strings);
111 localized_strings->SetString("optionsPageTitle",
112 l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE));
114 // Controlled settings bubble.
115 localized_strings->SetString("controlledSettingPolicy",
116 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_POLICY));
117 localized_strings->SetString("controlledSettingExtension",
118 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION));
119 localized_strings->SetString("controlledSettingExtensionWithName",
120 l10n_util::GetStringUTF16(
121 IDS_OPTIONS_CONTROLLED_SETTING_EXTENSION_WITH_NAME));
122 localized_strings->SetString("controlledSettingManageExtension",
123 l10n_util::GetStringUTF16(
124 IDS_OPTIONS_CONTROLLED_SETTING_MANAGE_EXTENSION));
125 localized_strings->SetString("controlledSettingDisableExtension",
126 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE));
127 localized_strings->SetString("controlledSettingRecommended",
128 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTING_RECOMMENDED));
129 localized_strings->SetString("controlledSettingHasRecommendation",
130 l10n_util::GetStringUTF16(
131 IDS_OPTIONS_CONTROLLED_SETTING_HAS_RECOMMENDATION));
132 localized_strings->SetString("controlledSettingFollowRecommendation",
133 l10n_util::GetStringUTF16(
134 IDS_OPTIONS_CONTROLLED_SETTING_FOLLOW_RECOMMENDATION));
135 localized_strings->SetString("controlledSettingsPolicy",
136 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_POLICY));
137 localized_strings->SetString("controlledSettingsExtension",
138 l10n_util::GetStringUTF16(IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION));
139 localized_strings->SetString("controlledSettingsExtensionWithName",
140 l10n_util::GetStringUTF16(
141 IDS_OPTIONS_CONTROLLED_SETTINGS_EXTENSION_WITH_NAME));
144 RegisterTitle(localized_strings, "searchPage", IDS_OPTIONS_SEARCH_PAGE_TITLE);
145 localized_strings->SetString("searchPlaceholder",
146 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PLACEHOLDER));
147 localized_strings->SetString("searchPageNoMatches",
148 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_NO_MATCHES));
149 localized_strings->SetString("searchPageHelpLabel",
150 l10n_util::GetStringUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_LABEL));
151 localized_strings->SetString("searchPageHelpTitle",
152 l10n_util::GetStringFUTF16(IDS_OPTIONS_SEARCH_PAGE_HELP_TITLE,
153 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
154 localized_strings->SetString("searchPageHelpURL",
155 chrome::kSettingsSearchHelpURL);
158 localized_strings->SetString("ok",
159 l10n_util::GetStringUTF16(IDS_OK));
160 localized_strings->SetString("cancel",
161 l10n_util::GetStringUTF16(IDS_CANCEL));
162 localized_strings->SetString("learnMore",
163 l10n_util::GetStringUTF16(IDS_LEARN_MORE));
164 localized_strings->SetString("close",
165 l10n_util::GetStringUTF16(IDS_CLOSE));
166 localized_strings->SetString("done",
167 l10n_util::GetStringUTF16(IDS_DONE));
168 localized_strings->SetString("deletableItemDeleteButtonTitle",
169 l10n_util::GetStringUTF16(IDS_OPTIONS_DELETABLE_ITEM_DELETE_BUTTON));
172 void CoreOptionsHandler::Uninitialize() {
173 std::string last_pref;
174 for (PreferenceCallbackMap::const_iterator iter = pref_callback_map_.begin();
175 iter != pref_callback_map_.end();
177 if (last_pref != iter->first) {
178 StopObservingPref(iter->first);
179 last_pref = iter->first;
184 void CoreOptionsHandler::OnPreferenceChanged(PrefService* service,
185 const std::string& pref_name) {
186 if (pref_name == prefs::kClearPluginLSODataEnabled) {
187 // This preference is stored in Local State, not in the user preferences.
188 UpdateClearPluginLSOData();
191 if (pref_name == prefs::kPepperFlashSettingsEnabled) {
192 UpdatePepperFlashSettingsEnabled();
195 NotifyPrefChanged(pref_name, std::string());
198 void CoreOptionsHandler::RegisterMessages() {
199 registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs());
200 local_state_registrar_.Init(g_browser_process->local_state());
202 web_ui()->RegisterMessageCallback("coreOptionsInitialize",
203 base::Bind(&CoreOptionsHandler::HandleInitialize,
204 base::Unretained(this)));
205 web_ui()->RegisterMessageCallback("onFinishedLoadingOptions",
206 base::Bind(&CoreOptionsHandler::OnFinishedLoading,
207 base::Unretained(this)));
208 web_ui()->RegisterMessageCallback("fetchPrefs",
209 base::Bind(&CoreOptionsHandler::HandleFetchPrefs,
210 base::Unretained(this)));
211 web_ui()->RegisterMessageCallback("observePrefs",
212 base::Bind(&CoreOptionsHandler::HandleObservePrefs,
213 base::Unretained(this)));
214 web_ui()->RegisterMessageCallback("setBooleanPref",
215 base::Bind(&CoreOptionsHandler::HandleSetBooleanPref,
216 base::Unretained(this)));
217 web_ui()->RegisterMessageCallback("setIntegerPref",
218 base::Bind(&CoreOptionsHandler::HandleSetIntegerPref,
219 base::Unretained(this)));
220 web_ui()->RegisterMessageCallback("setDoublePref",
221 base::Bind(&CoreOptionsHandler::HandleSetDoublePref,
222 base::Unretained(this)));
223 web_ui()->RegisterMessageCallback("setStringPref",
224 base::Bind(&CoreOptionsHandler::HandleSetStringPref,
225 base::Unretained(this)));
226 web_ui()->RegisterMessageCallback("setURLPref",
227 base::Bind(&CoreOptionsHandler::HandleSetURLPref,
228 base::Unretained(this)));
229 web_ui()->RegisterMessageCallback("setListPref",
230 base::Bind(&CoreOptionsHandler::HandleSetListPref,
231 base::Unretained(this)));
232 web_ui()->RegisterMessageCallback("clearPref",
233 base::Bind(&CoreOptionsHandler::HandleClearPref,
234 base::Unretained(this)));
235 web_ui()->RegisterMessageCallback("coreOptionsUserMetricsAction",
236 base::Bind(&CoreOptionsHandler::HandleUserMetricsAction,
237 base::Unretained(this)));
238 web_ui()->RegisterMessageCallback("disableExtension",
239 base::Bind(&CoreOptionsHandler::HandleDisableExtension,
240 base::Unretained(this)));
243 void CoreOptionsHandler::HandleInitialize(const base::ListValue* args) {
244 DCHECK(handlers_host_);
245 handlers_host_->InitializeHandlers();
248 void CoreOptionsHandler::OnFinishedLoading(const base::ListValue* args) {
249 DCHECK(handlers_host_);
250 handlers_host_->OnFinishedLoading();
253 base::Value* CoreOptionsHandler::FetchPref(const std::string& pref_name) {
254 return CreateValueForPref(pref_name, std::string());
257 void CoreOptionsHandler::ObservePref(const std::string& pref_name) {
258 if (g_browser_process->local_state()->FindPreference(pref_name.c_str())) {
259 local_state_registrar_.Add(
261 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
262 base::Unretained(this),
263 local_state_registrar_.prefs()));
265 // TODO(pneubeck): change this to if/else once kProxy is only used as a user
266 // pref. Currently, it is both a user and a local state pref.
267 if (Profile::FromWebUI(web_ui())->GetPrefs()->FindPreference(
268 pref_name.c_str())) {
271 base::Bind(&CoreOptionsHandler::OnPreferenceChanged,
272 base::Unretained(this),
273 registrar_.prefs()));
277 void CoreOptionsHandler::StopObservingPref(const std::string& pref_name) {
278 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
279 local_state_registrar_.Remove(pref_name.c_str());
281 registrar_.Remove(pref_name.c_str());
284 void CoreOptionsHandler::SetPref(const std::string& pref_name,
285 const base::Value* value,
286 const std::string& metric) {
287 PrefService* pref_service = FindServiceForPref(pref_name);
288 PrefChangeFilterMap::iterator iter = pref_change_filters_.find(pref_name);
289 if (iter != pref_change_filters_.end()) {
290 // Also check if the pref is user modifiable (don't even try to run the
291 // filter function if the user is not allowed to change the pref).
292 const PrefService::Preference* pref =
293 pref_service->FindPreference(pref_name.c_str());
294 if ((pref && !pref->IsUserModifiable()) || !iter->second.Run(value)) {
295 // Reject the change; remind the page of the true value.
296 NotifyPrefChanged(pref_name, std::string());
301 switch (value->GetType()) {
302 case base::Value::TYPE_BOOLEAN:
303 case base::Value::TYPE_INTEGER:
304 case base::Value::TYPE_DOUBLE:
305 case base::Value::TYPE_STRING:
306 case base::Value::TYPE_LIST:
307 pref_service->Set(pref_name.c_str(), *value);
315 ProcessUserMetric(value, metric);
318 void CoreOptionsHandler::ClearPref(const std::string& pref_name,
319 const std::string& metric) {
320 PrefService* pref_service = FindServiceForPref(pref_name);
321 pref_service->ClearPref(pref_name.c_str());
324 content::RecordComputedAction(metric);
327 void CoreOptionsHandler::ProcessUserMetric(const base::Value* value,
328 const std::string& metric) {
332 std::string metric_string = metric;
333 if (value->IsType(base::Value::TYPE_BOOLEAN)) {
335 CHECK(value->GetAsBoolean(&bool_value));
336 metric_string += bool_value ? "_Enable" : "_Disable";
339 content::RecordComputedAction(metric_string);
342 void CoreOptionsHandler::NotifyPrefChanged(
343 const std::string& pref_name,
344 const std::string& controlling_pref_name) {
345 scoped_ptr<base::Value> value(
346 CreateValueForPref(pref_name, controlling_pref_name));
347 DispatchPrefChangeNotification(pref_name, value.Pass());
350 void CoreOptionsHandler::DispatchPrefChangeNotification(
351 const std::string& name,
352 scoped_ptr<base::Value> value) {
353 std::pair<PreferenceCallbackMap::const_iterator,
354 PreferenceCallbackMap::const_iterator> range =
355 pref_callback_map_.equal_range(name);
356 base::ListValue result_value;
357 result_value.Append(new base::StringValue(name.c_str()));
358 result_value.Append(value.release());
359 for (PreferenceCallbackMap::const_iterator iter = range.first;
360 iter != range.second; ++iter) {
361 const std::string& callback_function = iter->second;
362 web_ui()->CallJavascriptFunction(callback_function, result_value);
366 base::Value* CoreOptionsHandler::CreateValueForPref(
367 const std::string& pref_name,
368 const std::string& controlling_pref_name) {
369 const PrefService* pref_service = FindServiceForPref(pref_name.c_str());
370 const PrefService::Preference* pref =
371 pref_service->FindPreference(pref_name.c_str());
374 return base::Value::CreateNullValue();
376 const PrefService::Preference* controlling_pref =
377 pref_service->FindPreference(controlling_pref_name.c_str());
378 if (!controlling_pref)
379 controlling_pref = pref;
381 base::DictionaryValue* dict = new base::DictionaryValue;
382 dict->Set("value", pref->GetValue()->DeepCopy());
383 if (controlling_pref->IsManaged()) {
384 dict->SetString("controlledBy", "policy");
385 } else if (controlling_pref->IsExtensionControlled() &&
386 CanSetExtensionControlledPrefValue(controlling_pref)) {
387 Profile* profile = Profile::FromWebUI(web_ui());
388 ExtensionPrefValueMap* extension_pref_value_map =
389 ExtensionPrefValueMapFactory::GetForBrowserContext(profile);
390 std::string extension_id =
391 extension_pref_value_map->GetExtensionControllingPref(
392 controlling_pref->name());
394 const extensions::Extension* extension =
395 extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
396 extension_id, extensions::ExtensionRegistry::EVERYTHING);
398 dict->SetString("controlledBy", "extension");
399 dict->Set("extension",
400 extensions::util::GetExtensionInfo(extension).release());
402 } else if (controlling_pref->IsRecommended()) {
403 dict->SetString("controlledBy", "recommended");
406 const base::Value* recommended_value =
407 controlling_pref->GetRecommendedValue();
408 if (recommended_value)
409 dict->Set("recommendedValue", recommended_value->DeepCopy());
410 dict->SetBoolean("disabled", !controlling_pref->IsUserModifiable());
414 PrefService* CoreOptionsHandler::FindServiceForPref(
415 const std::string& pref_name) {
416 // Proxy is a peculiar case: on ChromeOS, settings exist in both user
417 // prefs and local state, but chrome://settings should affect only user prefs.
418 // Elsewhere the proxy settings are stored in local state.
419 // See http://crbug.com/157147
420 PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
421 if (pref_name == prefs::kProxy)
422 #if defined(OS_CHROMEOS)
425 return g_browser_process->local_state();
428 // Find which PrefService contains the given pref. Pref names should not
429 // be duplicated across services, however if they are, prefer the user's
431 if (user_prefs->FindPreference(pref_name.c_str()))
434 if (g_browser_process->local_state()->FindPreference(pref_name.c_str()))
435 return g_browser_process->local_state();
440 void CoreOptionsHandler::HandleFetchPrefs(const base::ListValue* args) {
441 // First param is name of callback function, so, there needs to be at least
442 // one more element for the actual preference identifier.
443 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
445 // Get callback JS function name.
446 const base::Value* callback;
447 if (!args->Get(0, &callback) || !callback->IsType(base::Value::TYPE_STRING))
450 base::string16 callback_function;
451 if (!callback->GetAsString(&callback_function))
454 // Get the list of name for prefs to build the response dictionary.
455 base::DictionaryValue result_value;
456 const base::Value* list_member;
458 for (size_t i = 1; i < args->GetSize(); i++) {
459 if (!args->Get(i, &list_member))
462 if (!list_member->IsType(base::Value::TYPE_STRING))
465 std::string pref_name;
466 if (!list_member->GetAsString(&pref_name))
469 result_value.Set(pref_name.c_str(), FetchPref(pref_name));
471 web_ui()->CallJavascriptFunction(base::UTF16ToASCII(callback_function),
475 void CoreOptionsHandler::HandleObservePrefs(const base::ListValue* args) {
476 // First param is name is JS callback function name, the rest are pref
477 // identifiers that we are observing.
478 DCHECK_GE(static_cast<int>(args->GetSize()), 2);
480 // Get preference change callback function name.
481 std::string callback_func_name;
482 if (!args->GetString(0, &callback_func_name))
485 // Get all other parameters - pref identifiers.
486 for (size_t i = 1; i < args->GetSize(); i++) {
487 const base::Value* list_member;
488 if (!args->Get(i, &list_member))
491 // Just ignore bad pref identifiers for now.
492 std::string pref_name;
493 if (!list_member->IsType(base::Value::TYPE_STRING) ||
494 !list_member->GetAsString(&pref_name))
497 if (pref_callback_map_.find(pref_name) == pref_callback_map_.end())
498 ObservePref(pref_name);
500 pref_callback_map_.insert(
501 PreferenceCallbackMap::value_type(pref_name, callback_func_name));
505 void CoreOptionsHandler::HandleSetBooleanPref(const base::ListValue* args) {
506 HandleSetPref(args, TYPE_BOOLEAN);
509 void CoreOptionsHandler::HandleSetIntegerPref(const base::ListValue* args) {
510 HandleSetPref(args, TYPE_INTEGER);
513 void CoreOptionsHandler::HandleSetDoublePref(const base::ListValue* args) {
514 HandleSetPref(args, TYPE_DOUBLE);
517 void CoreOptionsHandler::HandleSetStringPref(const base::ListValue* args) {
518 HandleSetPref(args, TYPE_STRING);
521 void CoreOptionsHandler::HandleSetURLPref(const base::ListValue* args) {
522 HandleSetPref(args, TYPE_URL);
525 void CoreOptionsHandler::HandleSetListPref(const base::ListValue* args) {
526 HandleSetPref(args, TYPE_LIST);
529 void CoreOptionsHandler::HandleSetPref(const base::ListValue* args,
531 DCHECK_GT(static_cast<int>(args->GetSize()), 1);
533 std::string pref_name;
534 if (!args->GetString(0, &pref_name))
537 const base::Value* value;
538 if (!args->Get(1, &value))
541 scoped_ptr<base::Value> temp_value;
545 if (!value->IsType(base::Value::TYPE_BOOLEAN)) {
551 // In JS all numbers are doubles.
553 if (!value->GetAsDouble(&double_value)) {
557 int int_value = static_cast<int>(double_value);
558 temp_value.reset(new base::FundamentalValue(int_value));
559 value = temp_value.get();
563 if (!value->IsType(base::Value::TYPE_DOUBLE)) {
569 if (!value->IsType(base::Value::TYPE_STRING)) {
575 std::string original;
576 if (!value->GetAsString(&original)) {
580 GURL fixed = url_fixer::FixupURL(original, std::string());
581 temp_value.reset(new base::StringValue(fixed.spec()));
582 value = temp_value.get();
586 // In case we have a List pref we got a JSON string.
587 std::string json_string;
588 if (!value->GetAsString(&json_string)) {
593 base::JSONReader::Read(json_string));
594 value = temp_value.get();
595 if (!value->IsType(base::Value::TYPE_LIST)) {
606 if (args->GetSize() > 2 && !args->GetString(2, &metric))
607 LOG(WARNING) << "Invalid metric parameter: " << pref_name;
608 SetPref(pref_name, value, metric);
611 void CoreOptionsHandler::HandleClearPref(const base::ListValue* args) {
612 DCHECK_GT(static_cast<int>(args->GetSize()), 0);
614 std::string pref_name;
615 if (!args->GetString(0, &pref_name))
619 if (args->GetSize() > 1) {
620 if (!args->GetString(1, &metric))
624 ClearPref(pref_name, metric);
627 void CoreOptionsHandler::HandleUserMetricsAction(const base::ListValue* args) {
628 std::string metric = base::UTF16ToUTF8(ExtractStringValue(args));
630 content::RecordComputedAction(metric);
633 void CoreOptionsHandler::HandleDisableExtension(const base::ListValue* args) {
634 std::string extension_id;
635 if (args->GetString(0, &extension_id)) {
636 ExtensionService* extension_service = extensions::ExtensionSystem::Get(
637 Profile::FromWebUI(web_ui()))->extension_service();
638 DCHECK(extension_service);
639 extension_service->DisableExtension(
640 extension_id, extensions::Extension::DISABLE_USER_ACTION);
646 void CoreOptionsHandler::UpdateClearPluginLSOData() {
647 base::FundamentalValue enabled(
648 plugin_status_pref_setter_.IsClearPluginLSODataEnabled());
649 web_ui()->CallJavascriptFunction(
650 "OptionsPage.setClearPluginLSODataEnabled", enabled);
653 void CoreOptionsHandler::UpdatePepperFlashSettingsEnabled() {
654 base::FundamentalValue enabled(
655 plugin_status_pref_setter_.IsPepperFlashSettingsEnabled());
656 web_ui()->CallJavascriptFunction(
657 "OptionsPage.setPepperFlashSettingsEnabled", enabled);
660 } // namespace options