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. 4 5 #include "chrome/browser/ui/webui/options/core_options_handler.h" 6 7 #include "base/bind.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" 31 #include "url/gurl.h" 32 33 using content::UserMetricsAction; 34 35 namespace options { 36 37 namespace { 38 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) { 42 bool enable; 43 if (!to_value->GetAsBoolean(&enable)) { 44 NOTREACHED(); 45 return false; 46 } 47 48 return enable == OptionsUtil::ResolveMetricsReportingEnabled(enable); 49 } 50 51 } // namespace 52 53 CoreOptionsHandler::CoreOptionsHandler() 54 : handlers_host_(NULL) { 55 } 56 57 CoreOptionsHandler::~CoreOptionsHandler() {} 58 59 void CoreOptionsHandler::InitializeHandler() { 60 Profile* profile = Profile::FromWebUI(web_ui()); 61 62 plugin_status_pref_setter_.Init( 63 profile, 64 base::Bind(&CoreOptionsHandler::OnPreferenceChanged, 65 base::Unretained(this), 66 profile->GetPrefs())); 67 68 pref_change_filters_[prefs::kMetricsReportingEnabled] = 69 base::Bind(&AllowMetricsReportingChange); 70 } 71 72 void CoreOptionsHandler::InitializePage() { 73 UpdateClearPluginLSOData(); 74 UpdatePepperFlashSettingsEnabled(); 75 } 76 77 void CoreOptionsHandler::GetLocalizedValues( 78 DictionaryValue* localized_strings) { 79 GetStaticLocalizedValues(localized_strings); 80 } 81 82 void CoreOptionsHandler::GetStaticLocalizedValues( 83 base::DictionaryValue* localized_strings) { 84 DCHECK(localized_strings); 85 // Main 86 localized_strings->SetString("optionsPageTitle", 87 l10n_util::GetStringUTF16(IDS_SETTINGS_TITLE)); 88 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)); 106 107 // Search 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); 120 121 // Common 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)); 132 } 133 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(); 138 ++iter) { 139 if (last_pref != iter->first) { 140 StopObservingPref(iter->first); 141 last_pref = iter->first; 142 } 143 } 144 } 145 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(); 151 return; 152 } 153 if (pref_name == prefs::kPepperFlashSettingsEnabled) { 154 UpdatePepperFlashSettingsEnabled(); 155 return; 156 } 157 NotifyPrefChanged(pref_name, std::string()); 158 } 159 160 void CoreOptionsHandler::RegisterMessages() { 161 registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs()); 162 local_state_registrar_.Init(g_browser_process->local_state()); 163 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))); 197 } 198 199 void CoreOptionsHandler::HandleInitialize(const ListValue* args) { 200 DCHECK(handlers_host_); 201 handlers_host_->InitializeHandlers(); 202 } 203 204 base::Value* CoreOptionsHandler::FetchPref(const std::string& pref_name) { 205 return CreateValueForPref(pref_name, std::string()); 206 } 207 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( 211 pref_name.c_str(), 212 base::Bind(&CoreOptionsHandler::OnPreferenceChanged, 213 base::Unretained(this), 214 local_state_registrar_.prefs())); 215 } 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())) { 220 registrar_.Add( 221 pref_name.c_str(), 222 base::Bind(&CoreOptionsHandler::OnPreferenceChanged, 223 base::Unretained(this), 224 registrar_.prefs())); 225 } 226 } 227 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()); 231 else 232 registrar_.Remove(pref_name.c_str()); 233 } 234 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()); 248 return; 249 } 250 } 251 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); 259 break; 260 261 default: 262 NOTREACHED(); 263 return; 264 } 265 266 ProcessUserMetric(value, metric); 267 } 268 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()); 273 274 if (!metric.empty()) 275 content::RecordComputedAction(metric); 276 } 277 278 void CoreOptionsHandler::ProcessUserMetric(const base::Value* value, 279 const std::string& metric) { 280 if (metric.empty()) 281 return; 282 283 std::string metric_string = metric; 284 if (value->IsType(base::Value::TYPE_BOOLEAN)) { 285 bool bool_value; 286 CHECK(value->GetAsBoolean(&bool_value)); 287 metric_string += bool_value ? "_Enable" : "_Disable"; 288 } 289 290 content::RecordComputedAction(metric_string); 291 } 292 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()); 299 } 300 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); 314 } 315 } 316 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()); 323 if (!pref) { 324 NOTREACHED(); 325 return base::Value::CreateNullValue(); 326 } 327 const PrefService::Preference* controlling_pref = 328 pref_service->FindPreference(controlling_pref_name.c_str()); 329 if (!controlling_pref) 330 controlling_pref = pref; 331 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"); 340 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()); 346 return dict; 347 } 348 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) 358 return user_prefs; 359 #else 360 return g_browser_process->local_state(); 361 #endif 362 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 365 // prefs. 366 if (user_prefs->FindPreference(pref_name.c_str())) 367 return user_prefs; 368 369 if (g_browser_process->local_state()->FindPreference(pref_name.c_str())) 370 return g_browser_process->local_state(); 371 372 return user_prefs; 373 } 374 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); 379 380 // Get callback JS function name. 381 const base::Value* callback; 382 if (!args->Get(0, &callback) || !callback->IsType(base::Value::TYPE_STRING)) 383 return; 384 385 string16 callback_function; 386 if (!callback->GetAsString(&callback_function)) 387 return; 388 389 // Get the list of name for prefs to build the response dictionary. 390 DictionaryValue result_value; 391 const base::Value* list_member; 392 393 for (size_t i = 1; i < args->GetSize(); i++) { 394 if (!args->Get(i, &list_member)) 395 break; 396 397 if (!list_member->IsType(base::Value::TYPE_STRING)) 398 continue; 399 400 std::string pref_name; 401 if (!list_member->GetAsString(&pref_name)) 402 continue; 403 404 result_value.Set(pref_name.c_str(), FetchPref(pref_name)); 405 } 406 web_ui()->CallJavascriptFunction(UTF16ToASCII(callback_function), 407 result_value); 408 } 409 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); 414 415 // Get preference change callback function name. 416 std::string callback_func_name; 417 if (!args->GetString(0, &callback_func_name)) 418 return; 419 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)) 424 break; 425 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)) 430 continue; 431 432 if (pref_callback_map_.find(pref_name) == pref_callback_map_.end()) 433 ObservePref(pref_name); 434 435 pref_callback_map_.insert( 436 PreferenceCallbackMap::value_type(pref_name, callback_func_name)); 437 } 438 } 439 440 void CoreOptionsHandler::HandleSetBooleanPref(const ListValue* args) { 441 HandleSetPref(args, TYPE_BOOLEAN); 442 } 443 444 void CoreOptionsHandler::HandleSetIntegerPref(const ListValue* args) { 445 HandleSetPref(args, TYPE_INTEGER); 446 } 447 448 void CoreOptionsHandler::HandleSetDoublePref(const ListValue* args) { 449 HandleSetPref(args, TYPE_DOUBLE); 450 } 451 452 void CoreOptionsHandler::HandleSetStringPref(const ListValue* args) { 453 HandleSetPref(args, TYPE_STRING); 454 } 455 456 void CoreOptionsHandler::HandleSetURLPref(const ListValue* args) { 457 HandleSetPref(args, TYPE_URL); 458 } 459 460 void CoreOptionsHandler::HandleSetListPref(const ListValue* args) { 461 HandleSetPref(args, TYPE_LIST); 462 } 463 464 void CoreOptionsHandler::HandleSetPref(const ListValue* args, PrefType type) { 465 DCHECK_GT(static_cast<int>(args->GetSize()), 1); 466 467 std::string pref_name; 468 if (!args->GetString(0, &pref_name)) 469 return; 470 471 const base::Value* value; 472 if (!args->Get(1, &value)) 473 return; 474 475 scoped_ptr<base::Value> temp_value; 476 477 switch (type) { 478 case TYPE_BOOLEAN: 479 if (!value->IsType(base::Value::TYPE_BOOLEAN)) { 480 NOTREACHED(); 481 return; 482 } 483 break; 484 case TYPE_INTEGER: { 485 // In JS all numbers are doubles. 486 double double_value; 487 if (!value->GetAsDouble(&double_value)) { 488 NOTREACHED(); 489 return; 490 } 491 int int_value = static_cast<int>(double_value); 492 temp_value.reset(new base::FundamentalValue(int_value)); 493 value = temp_value.get(); 494 break; 495 } 496 case TYPE_DOUBLE: 497 if (!value->IsType(base::Value::TYPE_DOUBLE)) { 498 NOTREACHED(); 499 return; 500 } 501 break; 502 case TYPE_STRING: 503 if (!value->IsType(base::Value::TYPE_STRING)) { 504 NOTREACHED(); 505 return; 506 } 507 break; 508 case TYPE_URL: { 509 std::string original; 510 if (!value->GetAsString(&original)) { 511 NOTREACHED(); 512 return; 513 } 514 GURL fixed = URLFixerUpper::FixupURL(original, std::string()); 515 temp_value.reset(new base::StringValue(fixed.spec())); 516 value = temp_value.get(); 517 break; 518 } 519 case TYPE_LIST: { 520 // In case we have a List pref we got a JSON string. 521 std::string json_string; 522 if (!value->GetAsString(&json_string)) { 523 NOTREACHED(); 524 return; 525 } 526 temp_value.reset( 527 base::JSONReader::Read(json_string)); 528 value = temp_value.get(); 529 if (!value->IsType(base::Value::TYPE_LIST)) { 530 NOTREACHED(); 531 return; 532 } 533 break; 534 } 535 default: 536 NOTREACHED(); 537 } 538 539 std::string metric; 540 if (args->GetSize() > 2 && !args->GetString(2, &metric)) 541 LOG(WARNING) << "Invalid metric parameter: " << pref_name; 542 SetPref(pref_name, value, metric); 543 } 544 545 void CoreOptionsHandler::HandleClearPref(const ListValue* args) { 546 DCHECK_GT(static_cast<int>(args->GetSize()), 0); 547 548 std::string pref_name; 549 if (!args->GetString(0, &pref_name)) 550 return; 551 552 std::string metric; 553 if (args->GetSize() > 1) { 554 if (!args->GetString(1, &metric)) 555 NOTREACHED(); 556 } 557 558 ClearPref(pref_name, metric); 559 } 560 561 void CoreOptionsHandler::HandleUserMetricsAction(const ListValue* args) { 562 std::string metric = UTF16ToUTF8(ExtractStringValue(args)); 563 if (!metric.empty()) 564 content::RecordComputedAction(metric); 565 } 566 567 void CoreOptionsHandler::UpdateClearPluginLSOData() { 568 base::FundamentalValue enabled( 569 plugin_status_pref_setter_.IsClearPluginLSODataEnabled()); 570 web_ui()->CallJavascriptFunction( 571 "OptionsPage.setClearPluginLSODataEnabled", enabled); 572 } 573 574 void CoreOptionsHandler::UpdatePepperFlashSettingsEnabled() { 575 base::FundamentalValue enabled( 576 plugin_status_pref_setter_.IsPepperFlashSettingsEnabled()); 577 web_ui()->CallJavascriptFunction( 578 "OptionsPage.setPepperFlashSettingsEnabled", enabled); 579 } 580 581 } // namespace options 582