pref_service.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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/prefs/pref_service.h" 6 7#include <algorithm> 8#include <string> 9 10#include "app/l10n_util.h" 11#ifndef ANDROID 12#include "base/command_line.h" 13#endif 14#include "base/file_path.h" 15#include "base/file_util.h" 16#include "base/logging.h" 17#include "base/message_loop.h" 18#include "base/metrics/histogram.h" 19#include "base/stl_util-inl.h" 20#include "base/string_number_conversions.h" 21#include "base/string_util.h" 22#include "base/sys_string_conversions.h" 23#include "base/utf_string_conversions.h" 24#include "build/build_config.h" 25#include "chrome/browser/browser_thread.h" 26#include "chrome/browser/profile.h" 27#ifndef ANDROID 28// Notifications do not compile on Android and are the cause 29// of most of the ANDROID guards in this file. 30#include "chrome/common/notification_service.h" 31#include "grit/chromium_strings.h" 32#include "grit/generated_resources.h" 33#endif 34 35namespace { 36 37// A helper function for RegisterLocalized*Pref that creates a Value* based on 38// the string value in the locale dll. Because we control the values in a 39// locale dll, this should always return a Value of the appropriate type. 40Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) { 41#ifndef ANDROID 42 std::string resource_string = l10n_util::GetStringUTF8(message_id); 43 DCHECK(!resource_string.empty()); 44 switch (type) { 45 case Value::TYPE_BOOLEAN: { 46 if ("true" == resource_string) 47 return Value::CreateBooleanValue(true); 48 if ("false" == resource_string) 49 return Value::CreateBooleanValue(false); 50 break; 51 } 52 53 case Value::TYPE_INTEGER: { 54 int val; 55 base::StringToInt(resource_string, &val); 56 return Value::CreateIntegerValue(val); 57 } 58 59 case Value::TYPE_REAL: { 60 double val; 61 base::StringToDouble(resource_string, &val); 62 return Value::CreateRealValue(val); 63 } 64 65 case Value::TYPE_STRING: { 66 return Value::CreateStringValue(resource_string); 67 } 68 69 default: { 70 NOTREACHED() << 71 "list and dictionary types cannot have default locale values"; 72 } 73 } 74#endif 75 NOTREACHED(); 76 return Value::CreateNullValue(); 77} 78 79// Forwards a notification after a PostMessage so that we can wait for the 80// MessageLoop to run. 81void NotifyReadError(PrefService* pref, int message_id) { 82#ifndef ANDROID 83 Source<PrefService> source(pref); 84 NotificationService::current()->Notify(NotificationType::PROFILE_ERROR, 85 source, Details<int>(&message_id)); 86#endif 87} 88 89} // namespace 90 91// static 92PrefService* PrefService::CreatePrefService(const FilePath& pref_filename, 93 Profile* profile) { 94#if defined(OS_LINUX) && !defined(ANDROID) 95 // We'd like to see what fraction of our users have the preferences 96 // stored on a network file system, as we've had no end of troubles 97 // with NFS/AFS. 98 // TODO(evanm): remove this once we've collected state. 99 file_util::FileSystemType fstype; 100 if (file_util::GetFileSystemType(pref_filename.DirName(), &fstype)) { 101 UMA_HISTOGRAM_ENUMERATION("PrefService.FileSystemType", 102 static_cast<int>(fstype), 103 file_util::FILE_SYSTEM_TYPE_COUNT); 104 } 105#endif 106 107 return new PrefService( 108 PrefValueStore::CreatePrefValueStore(pref_filename, profile, false)); 109} 110 111// static 112PrefService* PrefService::CreateUserPrefService(const FilePath& pref_filename) { 113 return new PrefService( 114 PrefValueStore::CreatePrefValueStore(pref_filename, NULL, true)); 115} 116 117PrefService::PrefService(PrefValueStore* pref_value_store) 118 : pref_value_store_(pref_value_store) { 119#ifndef ANDROID 120 pref_notifier_.reset(new PrefNotifier(this, pref_value_store)); 121#endif 122 InitFromStorage(); 123} 124 125PrefService::~PrefService() { 126 DCHECK(CalledOnValidThread()); 127 STLDeleteContainerPointers(prefs_.begin(), prefs_.end()); 128 prefs_.clear(); 129} 130 131void PrefService::InitFromStorage() { 132#ifndef ANDROID 133 PrefStore::PrefReadError error = LoadPersistentPrefs(); 134 if (error == PrefStore::PREF_READ_ERROR_NONE) 135 return; 136 137 // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for 138 // an example problem that this can cause. 139 // Do some diagnosis and try to avoid losing data. 140 int message_id = 0; 141 if (error <= PrefStore::PREF_READ_ERROR_JSON_TYPE) { 142 message_id = IDS_PREFERENCES_CORRUPT_ERROR; 143 } else if (error != PrefStore::PREF_READ_ERROR_NO_FILE) { 144 message_id = IDS_PREFERENCES_UNREADABLE_ERROR; 145 } 146 147 if (message_id) { 148 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 149 NewRunnableFunction(&NotifyReadError, this, message_id)); 150 } 151 UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error, 20); 152#endif 153} 154 155bool PrefService::ReloadPersistentPrefs() { 156 return (LoadPersistentPrefs() == PrefStore::PREF_READ_ERROR_NONE); 157} 158 159PrefStore::PrefReadError PrefService::LoadPersistentPrefs() { 160 DCHECK(CalledOnValidThread()); 161 162 PrefStore::PrefReadError pref_error = pref_value_store_->ReadPrefs(); 163 164 for (PreferenceSet::iterator it = prefs_.begin(); 165 it != prefs_.end(); ++it) { 166 (*it)->pref_service_ = this; 167 } 168 169 return pref_error; 170} 171 172bool PrefService::SavePersistentPrefs() { 173 DCHECK(CalledOnValidThread()); 174 175 return pref_value_store_->WritePrefs(); 176} 177 178void PrefService::ScheduleSavePersistentPrefs() { 179 DCHECK(CalledOnValidThread()); 180 181 pref_value_store_->ScheduleWritePrefs(); 182} 183 184void PrefService::RegisterBooleanPref(const char* path, 185 bool default_value) { 186 RegisterPreference(path, Value::CreateBooleanValue(default_value)); 187} 188 189void PrefService::RegisterIntegerPref(const char* path, int default_value) { 190 RegisterPreference(path, Value::CreateIntegerValue(default_value)); 191} 192 193void PrefService::RegisterRealPref(const char* path, double default_value) { 194 RegisterPreference(path, Value::CreateRealValue(default_value)); 195} 196 197void PrefService::RegisterStringPref(const char* path, 198 const std::string& default_value) { 199 RegisterPreference(path, Value::CreateStringValue(default_value)); 200} 201 202void PrefService::RegisterFilePathPref(const char* path, 203 const FilePath& default_value) { 204 RegisterPreference(path, Value::CreateStringValue(default_value.value())); 205} 206 207void PrefService::RegisterListPref(const char* path) { 208 RegisterPreference(path, new ListValue()); 209} 210 211void PrefService::RegisterDictionaryPref(const char* path) { 212 RegisterPreference(path, new DictionaryValue()); 213} 214 215void PrefService::RegisterLocalizedBooleanPref(const char* path, 216 int locale_default_message_id) { 217 RegisterPreference( 218 path, 219 CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id)); 220} 221 222void PrefService::RegisterLocalizedIntegerPref(const char* path, 223 int locale_default_message_id) { 224 RegisterPreference( 225 path, 226 CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id)); 227} 228 229void PrefService::RegisterLocalizedRealPref(const char* path, 230 int locale_default_message_id) { 231 RegisterPreference( 232 path, 233 CreateLocaleDefaultValue(Value::TYPE_REAL, locale_default_message_id)); 234} 235 236void PrefService::RegisterLocalizedStringPref(const char* path, 237 int locale_default_message_id) { 238 RegisterPreference( 239 path, 240 CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id)); 241} 242 243bool PrefService::GetBoolean(const char* path) const { 244 DCHECK(CalledOnValidThread()); 245 246 bool result = false; 247 248 const Preference* pref = FindPreference(path); 249 if (!pref) { 250 NOTREACHED() << "Trying to read an unregistered pref: " << path; 251 return result; 252 } 253 bool rv = pref->GetValue()->GetAsBoolean(&result); 254 DCHECK(rv); 255 return result; 256} 257 258int PrefService::GetInteger(const char* path) const { 259 DCHECK(CalledOnValidThread()); 260 261 int result = 0; 262 263 const Preference* pref = FindPreference(path); 264 if (!pref) { 265 NOTREACHED() << "Trying to read an unregistered pref: " << path; 266 return result; 267 } 268 bool rv = pref->GetValue()->GetAsInteger(&result); 269 DCHECK(rv); 270 return result; 271} 272 273double PrefService::GetReal(const char* path) const { 274 DCHECK(CalledOnValidThread()); 275 276 double result = 0.0; 277 278 const Preference* pref = FindPreference(path); 279 if (!pref) { 280 NOTREACHED() << "Trying to read an unregistered pref: " << path; 281 return result; 282 } 283 bool rv = pref->GetValue()->GetAsReal(&result); 284 DCHECK(rv); 285 return result; 286} 287 288std::string PrefService::GetString(const char* path) const { 289 DCHECK(CalledOnValidThread()); 290 291 std::string result; 292 293 const Preference* pref = FindPreference(path); 294 if (!pref) { 295 NOTREACHED() << "Trying to read an unregistered pref: " << path; 296 return result; 297 } 298 bool rv = pref->GetValue()->GetAsString(&result); 299 DCHECK(rv); 300 return result; 301} 302 303FilePath PrefService::GetFilePath(const char* path) const { 304 DCHECK(CalledOnValidThread()); 305 306 FilePath::StringType result; 307 308 const Preference* pref = FindPreference(path); 309 if (!pref) { 310 NOTREACHED() << "Trying to read an unregistered pref: " << path; 311 return FilePath(result); 312 } 313 bool rv = pref->GetValue()->GetAsString(&result); 314 DCHECK(rv); 315#if defined(OS_POSIX) 316 // We store filepaths as UTF8, so convert it back to the system type. 317 result = base::SysWideToNativeMB(UTF8ToWide(result)); 318#endif 319 return FilePath(result); 320} 321 322bool PrefService::HasPrefPath(const char* path) const { 323 return pref_value_store_->HasPrefPath(path); 324} 325 326const PrefService::Preference* PrefService::FindPreference( 327 const char* pref_name) const { 328 DCHECK(CalledOnValidThread()); 329 Preference p(this, pref_name); 330 PreferenceSet::const_iterator it = prefs_.find(&p); 331 return it == prefs_.end() ? NULL : *it; 332} 333 334bool PrefService::IsManagedPreference(const char* pref_name) const { 335 const Preference* pref = FindPreference(pref_name); 336 if (pref && pref->IsManaged()) { 337 return true; 338 } 339 return false; 340} 341 342const DictionaryValue* PrefService::GetDictionary(const char* path) const { 343 DCHECK(CalledOnValidThread()); 344 345 const Preference* pref = FindPreference(path); 346 if (!pref) { 347 NOTREACHED() << "Trying to read an unregistered pref: " << path; 348 return NULL; 349 } 350 const Value* value = pref->GetValue(); 351 if (value->GetType() == Value::TYPE_NULL) 352 return NULL; 353 return static_cast<const DictionaryValue*>(value); 354} 355 356const ListValue* PrefService::GetList(const char* path) const { 357 DCHECK(CalledOnValidThread()); 358 359 const Preference* pref = FindPreference(path); 360 if (!pref) { 361 NOTREACHED() << "Trying to read an unregistered pref: " << path; 362 return NULL; 363 } 364 const Value* value = pref->GetValue(); 365 if (value->GetType() == Value::TYPE_NULL) 366 return NULL; 367 return static_cast<const ListValue*>(value); 368} 369 370#ifndef ANDROID 371void PrefService::AddPrefObserver(const char* path, 372 NotificationObserver* obs) { 373 pref_notifier_->AddPrefObserver(path, obs); 374} 375 376void PrefService::RemovePrefObserver(const char* path, 377 NotificationObserver* obs) { 378 pref_notifier_->RemovePrefObserver(path, obs); 379} 380#endif 381 382void PrefService::RegisterPreference(const char* path, Value* default_value) { 383 DCHECK(CalledOnValidThread()); 384 385 // The main code path takes ownership, but most don't. We'll be safe. 386 scoped_ptr<Value> scoped_value(default_value); 387 388 if (FindPreference(path)) { 389 NOTREACHED() << "Tried to register duplicate pref " << path; 390 return; 391 } 392 393 Value::ValueType orig_type = default_value->GetType(); 394 DCHECK(orig_type != Value::TYPE_NULL && orig_type != Value::TYPE_BINARY) << 395 "invalid preference type: " << orig_type; 396 397 // We set the default value of dictionaries and lists to be null so it's 398 // easier for callers to check for empty dict/list prefs. The PrefValueStore 399 // accepts ownership of the value (null or default_value). 400 if (Value::TYPE_LIST == orig_type || Value::TYPE_DICTIONARY == orig_type) { 401 pref_value_store_->SetDefaultPrefValue(path, Value::CreateNullValue()); 402 } else { 403 // Hand off ownership. 404 DCHECK(!PrefStore::IsUseDefaultSentinelValue(default_value)); 405 pref_value_store_->SetDefaultPrefValue(path, scoped_value.release()); 406 } 407 408 pref_value_store_->RegisterPreferenceType(path, orig_type); 409 prefs_.insert(new Preference(this, path)); 410} 411 412void PrefService::ClearPref(const char* path) { 413 DCHECK(CalledOnValidThread()); 414 415 const Preference* pref = FindPreference(path); 416 if (!pref) { 417 NOTREACHED() << "Trying to clear an unregistered pref: " << path; 418 return; 419 } 420#ifndef ANDROID 421 if (pref_value_store_->RemoveUserPrefValue(path)) 422 pref_notifier_->OnUserPreferenceSet(path); 423#endif 424} 425 426void PrefService::Set(const char* path, const Value& value) { 427 DCHECK(CalledOnValidThread()); 428 429 const Preference* pref = FindPreference(path); 430 if (!pref) { 431 NOTREACHED() << "Trying to write an unregistered pref: " << path; 432 return; 433 } 434 435 // Allow dictionary and list types to be set to null, which removes their 436 // user values. 437 bool value_changed = false; 438 if (value.GetType() == Value::TYPE_NULL && 439 (pref->GetType() == Value::TYPE_DICTIONARY || 440 pref->GetType() == Value::TYPE_LIST)) { 441 value_changed = pref_value_store_->RemoveUserPrefValue(path); 442 } else if (pref->GetType() != value.GetType()) { 443 NOTREACHED() << "Trying to set pref " << path 444 << " of type " << pref->GetType() 445 << " to value of type " << value.GetType(); 446 } else { 447 value_changed = pref_value_store_->SetUserPrefValue(path, value.DeepCopy()); 448 } 449#ifndef ANDROID 450 if (value_changed) 451 pref_notifier_->OnUserPreferenceSet(path); 452#endif 453} 454 455void PrefService::SetBoolean(const char* path, bool value) { 456 SetUserPrefValue(path, Value::CreateBooleanValue(value)); 457} 458 459void PrefService::SetInteger(const char* path, int value) { 460 SetUserPrefValue(path, Value::CreateIntegerValue(value)); 461} 462 463void PrefService::SetReal(const char* path, double value) { 464 SetUserPrefValue(path, Value::CreateRealValue(value)); 465} 466 467void PrefService::SetString(const char* path, const std::string& value) { 468 SetUserPrefValue(path, Value::CreateStringValue(value)); 469} 470 471void PrefService::SetFilePath(const char* path, const FilePath& value) { 472#if defined(OS_POSIX) 473 // Value::SetString only knows about UTF8 strings, so convert the path from 474 // the system native value to UTF8. 475 std::string path_utf8 = WideToUTF8(base::SysNativeMBToWide(value.value())); 476 Value* new_value = Value::CreateStringValue(path_utf8); 477#else 478 Value* new_value = Value::CreateStringValue(value.value()); 479#endif 480 481 SetUserPrefValue(path, new_value); 482} 483 484void PrefService::SetInt64(const char* path, int64 value) { 485 SetUserPrefValue(path, Value::CreateStringValue(base::Int64ToString(value))); 486} 487 488int64 PrefService::GetInt64(const char* path) const { 489 DCHECK(CalledOnValidThread()); 490 491 const Preference* pref = FindPreference(path); 492 if (!pref) { 493 NOTREACHED() << "Trying to read an unregistered pref: " << path; 494 return 0; 495 } 496 std::string result("0"); 497 bool rv = pref->GetValue()->GetAsString(&result); 498 DCHECK(rv); 499 500 int64 val; 501 base::StringToInt64(result, &val); 502 return val; 503} 504 505void PrefService::RegisterInt64Pref(const char* path, int64 default_value) { 506 RegisterPreference( 507 path, Value::CreateStringValue(base::Int64ToString(default_value))); 508} 509 510DictionaryValue* PrefService::GetMutableDictionary(const char* path) { 511 DCHECK(CalledOnValidThread()); 512 513 const Preference* pref = FindPreference(path); 514 if (!pref) { 515 NOTREACHED() << "Trying to get an unregistered pref: " << path; 516 return NULL; 517 } 518 if (pref->GetType() != Value::TYPE_DICTIONARY) { 519 NOTREACHED() << "Wrong type for GetMutableDictionary: " << path; 520 return NULL; 521 } 522 523 DictionaryValue* dict = NULL; 524 Value* tmp_value = NULL; 525 // Look for an existing preference in the user store. If it doesn't 526 // exist or isn't the correct type, create a new user preference. 527 if (!pref_value_store_->GetUserValue(path, &tmp_value) || 528 !tmp_value->IsType(Value::TYPE_DICTIONARY)) { 529 dict = new DictionaryValue; 530 pref_value_store_->SetUserPrefValue(path, dict); 531 } else { 532 dict = static_cast<DictionaryValue*>(tmp_value); 533 } 534 return dict; 535} 536 537ListValue* PrefService::GetMutableList(const char* path) { 538 DCHECK(CalledOnValidThread()); 539 540 const Preference* pref = FindPreference(path); 541 if (!pref) { 542 NOTREACHED() << "Trying to get an unregistered pref: " << path; 543 return NULL; 544 } 545 if (pref->GetType() != Value::TYPE_LIST) { 546 NOTREACHED() << "Wrong type for GetMutableList: " << path; 547 return NULL; 548 } 549 550 ListValue* list = NULL; 551 Value* tmp_value = NULL; 552 // Look for an existing preference in the user store. If it doesn't 553 // exist or isn't the correct type, create a new user preference. 554 if (!pref_value_store_->GetUserValue(path, &tmp_value) || 555 !tmp_value->IsType(Value::TYPE_LIST)) { 556 list = new ListValue; 557 pref_value_store_->SetUserPrefValue(path, list); 558 } else { 559 list = static_cast<ListValue*>(tmp_value); 560 } 561 return list; 562} 563 564Value* PrefService::GetPrefCopy(const char* path) { 565 DCHECK(CalledOnValidThread()); 566 567 const Preference* pref = FindPreference(path); 568 DCHECK(pref); 569 return pref->GetValue()->DeepCopy(); 570} 571 572void PrefService::SetUserPrefValue(const char* path, Value* new_value) { 573 DCHECK(CalledOnValidThread()); 574 575 const Preference* pref = FindPreference(path); 576 if (!pref) { 577 NOTREACHED() << "Trying to write an unregistered pref: " << path; 578 return; 579 } 580 if (pref->IsManaged()) { 581 NOTREACHED() << "Preference is managed: " << path; 582 return; 583 } 584 if (pref->GetType() != new_value->GetType()) { 585 NOTREACHED() << "Trying to set pref " << path 586 << " of type " << pref->GetType() 587 << " to value of type " << new_value->GetType(); 588 return; 589 } 590 591#ifndef ANDROID 592 if (pref_value_store_->SetUserPrefValue(path, new_value)) 593 pref_notifier_->OnUserPreferenceSet(path); 594#endif 595} 596 597/////////////////////////////////////////////////////////////////////////////// 598// PrefService::Preference 599 600PrefService::Preference::Preference(const PrefService* service, 601 const char* name) 602 : name_(name), 603 pref_service_(service) { 604 DCHECK(name); 605 DCHECK(service); 606} 607 608Value::ValueType PrefService::Preference::GetType() const { 609 return pref_service_->pref_value_store_->GetRegisteredType(name_); 610} 611 612const Value* PrefService::Preference::GetValue() const { 613 DCHECK(pref_service_->FindPreference(name_.c_str())) << 614 "Must register pref before getting its value"; 615 616 Value* found_value = NULL; 617 if (pref_service_->pref_value_store_->GetValue(name_, &found_value)) 618 return found_value; 619 620 // Every registered preference has at least a default value. 621 NOTREACHED() << "no valid value found for registered pref " << name_; 622 return NULL; 623} 624 625bool PrefService::Preference::IsManaged() const { 626 PrefValueStore* pref_value_store = 627 pref_service_->pref_value_store_; 628 return pref_value_store->PrefValueInManagedPlatformStore(name_.c_str()) || 629 pref_value_store->PrefValueInDeviceManagementStore(name_.c_str()); 630} 631 632bool PrefService::Preference::HasExtensionSetting() const { 633 return pref_service_->pref_value_store_-> 634 PrefValueInExtensionStore(name_.c_str()); 635} 636 637bool PrefService::Preference::HasUserSetting() const { 638 return pref_service_->pref_value_store_-> 639 PrefValueInUserStore(name_.c_str()); 640} 641 642bool PrefService::Preference::IsExtensionControlled() const { 643 return pref_service_->pref_value_store_-> 644 PrefValueFromExtensionStore(name_.c_str()); 645} 646 647bool PrefService::Preference::IsUserControlled() const { 648 return pref_service_->pref_value_store_-> 649 PrefValueFromUserStore(name_.c_str()); 650} 651 652bool PrefService::Preference::IsDefaultValue() const { 653 return pref_service_->pref_value_store_-> 654 PrefValueFromDefaultStore(name_.c_str()); 655} 656 657bool PrefService::Preference::IsUserModifiable() const { 658 return pref_service_->pref_value_store_-> 659 PrefValueUserModifiable(name_.c_str()); 660} 661