1// Copyright (c) 2011 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 "base/command_line.h" 11#include "base/file_path.h" 12#include "base/file_util.h" 13#include "base/logging.h" 14#include "base/message_loop.h" 15#include "base/metrics/histogram.h" 16#include "base/stl_util-inl.h" 17#include "base/string_number_conversions.h" 18#include "base/string_util.h" 19#include "base/value_conversions.h" 20#include "build/build_config.h" 21#include "chrome/browser/extensions/extension_pref_store.h" 22#include "chrome/browser/policy/configuration_policy_pref_store.h" 23#include "chrome/browser/prefs/command_line_pref_store.h" 24#include "chrome/browser/prefs/default_pref_store.h" 25#include "chrome/browser/prefs/overlay_persistent_pref_store.h" 26#include "chrome/browser/prefs/pref_notifier_impl.h" 27#include "chrome/browser/prefs/pref_value_store.h" 28#include "chrome/browser/ui/profile_error_dialog.h" 29#include "chrome/common/json_pref_store.h" 30#include "content/browser/browser_thread.h" 31#include "content/common/notification_service.h" 32#include "grit/chromium_strings.h" 33#include "grit/generated_resources.h" 34#include "ui/base/l10n/l10n_util.h" 35 36namespace { 37 38// A helper function for RegisterLocalized*Pref that creates a Value* based on 39// the string value in the locale dll. Because we control the values in a 40// locale dll, this should always return a Value of the appropriate type. 41Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) { 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_DOUBLE: { 60 double val; 61 base::StringToDouble(resource_string, &val); 62 return Value::CreateDoubleValue(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 NOTREACHED(); 75 return Value::CreateNullValue(); 76} 77 78// Forwards a notification after a PostMessage so that we can wait for the 79// MessageLoop to run. 80void NotifyReadError(PrefService* pref, int message_id) { 81 ShowProfileErrorDialog(message_id); 82} 83 84} // namespace 85 86// static 87PrefService* PrefService::CreatePrefService(const FilePath& pref_filename, 88 PrefStore* extension_prefs, 89 Profile* profile) { 90 return CreatePrefServiceAsync(pref_filename, extension_prefs, profile, NULL); 91} 92 93// static 94PrefService* PrefService::CreatePrefServiceAsync( 95 const FilePath& pref_filename, 96 PrefStore* extension_prefs, 97 Profile* profile, 98 PrefService::Delegate* delegate) { 99 using policy::ConfigurationPolicyPrefStore; 100 101#if defined(OS_LINUX) 102 // We'd like to see what fraction of our users have the preferences 103 // stored on a network file system, as we've had no end of troubles 104 // with NFS/AFS. 105 // TODO(evanm): remove this once we've collected state. 106 file_util::FileSystemType fstype; 107 if (file_util::GetFileSystemType(pref_filename.DirName(), &fstype)) { 108 UMA_HISTOGRAM_ENUMERATION("PrefService.FileSystemType", 109 static_cast<int>(fstype), 110 file_util::FILE_SYSTEM_TYPE_COUNT); 111 } 112#endif 113 114 ConfigurationPolicyPrefStore* managed_platform = 115 ConfigurationPolicyPrefStore::CreateManagedPlatformPolicyPrefStore(); 116 ConfigurationPolicyPrefStore* managed_cloud = 117 ConfigurationPolicyPrefStore::CreateManagedCloudPolicyPrefStore(profile); 118 CommandLinePrefStore* command_line = 119 new CommandLinePrefStore(CommandLine::ForCurrentProcess()); 120 JsonPrefStore* user = new JsonPrefStore( 121 pref_filename, 122 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); 123 ConfigurationPolicyPrefStore* recommended_platform = 124 ConfigurationPolicyPrefStore::CreateRecommendedPlatformPolicyPrefStore(); 125 ConfigurationPolicyPrefStore* recommended_cloud = 126 ConfigurationPolicyPrefStore::CreateRecommendedCloudPolicyPrefStore( 127 profile); 128 DefaultPrefStore* default_pref_store = new DefaultPrefStore(); 129 130 return new PrefService(managed_platform, managed_cloud, extension_prefs, 131 command_line, user, recommended_platform, 132 recommended_cloud, default_pref_store, delegate); 133} 134 135PrefService* PrefService::CreateIncognitoPrefService( 136 PrefStore* incognito_extension_prefs) { 137 return new PrefService(*this, incognito_extension_prefs); 138} 139 140PrefService::PrefService(PrefStore* managed_platform_prefs, 141 PrefStore* managed_cloud_prefs, 142 PrefStore* extension_prefs, 143 PrefStore* command_line_prefs, 144 PersistentPrefStore* user_prefs, 145 PrefStore* recommended_platform_prefs, 146 PrefStore* recommended_cloud_prefs, 147 DefaultPrefStore* default_store, 148 PrefService::Delegate* delegate) 149 : user_pref_store_(user_prefs), 150 default_store_(default_store), 151 delegate_(delegate) { 152 pref_notifier_.reset(new PrefNotifierImpl(this)); 153 pref_value_store_.reset( 154 new PrefValueStore(managed_platform_prefs, 155 managed_cloud_prefs, 156 extension_prefs, 157 command_line_prefs, 158 user_pref_store_, 159 recommended_platform_prefs, 160 recommended_cloud_prefs, 161 default_store, 162 pref_notifier_.get())); 163 InitFromStorage(); 164} 165 166PrefService::PrefService(const PrefService& original, 167 PrefStore* incognito_extension_prefs) 168 : user_pref_store_( 169 new OverlayPersistentPrefStore(original.user_pref_store_.get())), 170 default_store_(original.default_store_.get()), 171 delegate_(NULL) { 172 pref_notifier_.reset(new PrefNotifierImpl(this)); 173 pref_value_store_.reset(original.pref_value_store_->CloneAndSpecialize( 174 NULL, // managed_platform_prefs 175 NULL, // managed_cloud_prefs 176 incognito_extension_prefs, 177 NULL, // command_line_prefs 178 user_pref_store_.get(), 179 NULL, // recommended_platform_prefs 180 NULL, // recommended_cloud_prefs 181 default_store_.get(), 182 pref_notifier_.get())); 183 InitFromStorage(); 184} 185 186PrefService::~PrefService() { 187 DCHECK(CalledOnValidThread()); 188 STLDeleteContainerPointers(prefs_.begin(), prefs_.end()); 189 prefs_.clear(); 190 191 // Reset pointers so accesses after destruction reliably crash. 192 pref_value_store_.reset(); 193 user_pref_store_ = NULL; 194 default_store_ = NULL; 195} 196 197void PrefService::OnPrefsRead(PersistentPrefStore::PrefReadError error, 198 bool no_dir) { 199 if (no_dir) { 200 // Bad news. When profile is created, the process that creates the directory 201 // is explicitly started. So if directory is missing it probably means that 202 // Chromium hasn't sufficient privileges. 203 CHECK(delegate_); 204 delegate_->OnPrefsLoaded(this, false); 205 return; 206 } 207 208 if (error != PersistentPrefStore::PREF_READ_ERROR_NONE) { 209 // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for 210 // an example problem that this can cause. 211 // Do some diagnosis and try to avoid losing data. 212 int message_id = 0; 213 if (error <= PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE) { 214 message_id = IDS_PREFERENCES_CORRUPT_ERROR; 215 } else if (error != PersistentPrefStore::PREF_READ_ERROR_NO_FILE) { 216 message_id = IDS_PREFERENCES_UNREADABLE_ERROR; 217 } 218 219 if (message_id) { 220 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 221 NewRunnableFunction(&NotifyReadError, this, message_id)); 222 } 223 UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error, 20); 224 } 225 226 if (delegate_) 227 delegate_->OnPrefsLoaded(this, true); 228} 229 230void PrefService::InitFromStorage() { 231 if (!delegate_) { 232 const PersistentPrefStore::PrefReadError error = 233 user_pref_store_->ReadPrefs(); 234 OnPrefsRead(error, false); 235 } else { 236 // todo(altimofeev): move this method to PersistentPrefStore interface. 237 (static_cast<JsonPrefStore*>(user_pref_store_.get()))->ReadPrefs(this); 238 } 239} 240 241bool PrefService::ReloadPersistentPrefs() { 242 return user_pref_store_->ReadPrefs() == 243 PersistentPrefStore::PREF_READ_ERROR_NONE; 244} 245 246bool PrefService::SavePersistentPrefs() { 247 DCHECK(CalledOnValidThread()); 248 return user_pref_store_->WritePrefs(); 249} 250 251void PrefService::ScheduleSavePersistentPrefs() { 252 DCHECK(CalledOnValidThread()); 253 user_pref_store_->ScheduleWritePrefs(); 254} 255 256void PrefService::CommitPendingWrite() { 257 DCHECK(CalledOnValidThread()); 258 user_pref_store_->CommitPendingWrite(); 259} 260 261void PrefService::RegisterBooleanPref(const char* path, 262 bool default_value) { 263 RegisterPreference(path, Value::CreateBooleanValue(default_value)); 264} 265 266void PrefService::RegisterIntegerPref(const char* path, int default_value) { 267 RegisterPreference(path, Value::CreateIntegerValue(default_value)); 268} 269 270void PrefService::RegisterDoublePref(const char* path, double default_value) { 271 RegisterPreference(path, Value::CreateDoubleValue(default_value)); 272} 273 274void PrefService::RegisterStringPref(const char* path, 275 const std::string& default_value) { 276 RegisterPreference(path, Value::CreateStringValue(default_value)); 277} 278 279void PrefService::RegisterFilePathPref(const char* path, 280 const FilePath& default_value) { 281 RegisterPreference(path, Value::CreateStringValue(default_value.value())); 282} 283 284void PrefService::RegisterListPref(const char* path) { 285 RegisterPreference(path, new ListValue()); 286} 287 288void PrefService::RegisterListPref(const char* path, ListValue* default_value) { 289 RegisterPreference(path, default_value); 290} 291 292void PrefService::RegisterDictionaryPref(const char* path) { 293 RegisterPreference(path, new DictionaryValue()); 294} 295 296void PrefService::RegisterDictionaryPref(const char* path, 297 DictionaryValue* default_value) { 298 RegisterPreference(path, default_value); 299} 300 301void PrefService::RegisterLocalizedBooleanPref(const char* path, 302 int locale_default_message_id) { 303 RegisterPreference( 304 path, 305 CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id)); 306} 307 308void PrefService::RegisterLocalizedIntegerPref(const char* path, 309 int locale_default_message_id) { 310 RegisterPreference( 311 path, 312 CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id)); 313} 314 315void PrefService::RegisterLocalizedDoublePref(const char* path, 316 int locale_default_message_id) { 317 RegisterPreference( 318 path, 319 CreateLocaleDefaultValue(Value::TYPE_DOUBLE, locale_default_message_id)); 320} 321 322void PrefService::RegisterLocalizedStringPref(const char* path, 323 int locale_default_message_id) { 324 RegisterPreference( 325 path, 326 CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id)); 327} 328 329bool PrefService::GetBoolean(const char* path) const { 330 DCHECK(CalledOnValidThread()); 331 332 bool result = false; 333 334 const Preference* pref = FindPreference(path); 335 if (!pref) { 336 NOTREACHED() << "Trying to read an unregistered pref: " << path; 337 return result; 338 } 339 bool rv = pref->GetValue()->GetAsBoolean(&result); 340 DCHECK(rv); 341 return result; 342} 343 344int PrefService::GetInteger(const char* path) const { 345 DCHECK(CalledOnValidThread()); 346 347 int result = 0; 348 349 const Preference* pref = FindPreference(path); 350 if (!pref) { 351 NOTREACHED() << "Trying to read an unregistered pref: " << path; 352 return result; 353 } 354 bool rv = pref->GetValue()->GetAsInteger(&result); 355 DCHECK(rv); 356 return result; 357} 358 359double PrefService::GetDouble(const char* path) const { 360 DCHECK(CalledOnValidThread()); 361 362 double result = 0.0; 363 364 const Preference* pref = FindPreference(path); 365 if (!pref) { 366 NOTREACHED() << "Trying to read an unregistered pref: " << path; 367 return result; 368 } 369 bool rv = pref->GetValue()->GetAsDouble(&result); 370 DCHECK(rv); 371 return result; 372} 373 374std::string PrefService::GetString(const char* path) const { 375 DCHECK(CalledOnValidThread()); 376 377 std::string result; 378 379 const Preference* pref = FindPreference(path); 380 if (!pref) { 381 NOTREACHED() << "Trying to read an unregistered pref: " << path; 382 return result; 383 } 384 bool rv = pref->GetValue()->GetAsString(&result); 385 DCHECK(rv); 386 return result; 387} 388 389FilePath PrefService::GetFilePath(const char* path) const { 390 DCHECK(CalledOnValidThread()); 391 392 FilePath result; 393 394 const Preference* pref = FindPreference(path); 395 if (!pref) { 396 NOTREACHED() << "Trying to read an unregistered pref: " << path; 397 return FilePath(result); 398 } 399 bool rv = base::GetValueAsFilePath(*pref->GetValue(), &result); 400 DCHECK(rv); 401 return result; 402} 403 404bool PrefService::HasPrefPath(const char* path) const { 405 const Preference* pref = FindPreference(path); 406 return pref && !pref->IsDefaultValue(); 407} 408 409DictionaryValue* PrefService::GetPreferenceValues() const { 410 DCHECK(CalledOnValidThread()); 411 DictionaryValue* out = new DictionaryValue; 412 DefaultPrefStore::const_iterator i = default_store_->begin(); 413 for (; i != default_store_->end(); ++i) { 414 const Preference* pref = FindPreference(i->first.c_str()); 415 DCHECK(pref); 416 const Value* value = pref->GetValue(); 417 DCHECK(value); 418 out->Set(i->first, value->DeepCopy()); 419 } 420 return out; 421} 422 423const PrefService::Preference* PrefService::FindPreference( 424 const char* pref_name) const { 425 DCHECK(CalledOnValidThread()); 426 Preference p(this, pref_name, Value::TYPE_NULL); 427 PreferenceSet::const_iterator it = prefs_.find(&p); 428 if (it != prefs_.end()) 429 return *it; 430 const Value::ValueType type = default_store_->GetType(pref_name); 431 if (type == Value::TYPE_NULL) 432 return NULL; 433 Preference* new_pref = new Preference(this, pref_name, type); 434 prefs_.insert(new_pref); 435 return new_pref; 436} 437 438bool PrefService::ReadOnly() const { 439 return user_pref_store_->ReadOnly(); 440} 441 442bool PrefService::IsManagedPreference(const char* pref_name) const { 443 const Preference* pref = FindPreference(pref_name); 444 return pref && pref->IsManaged(); 445} 446 447const DictionaryValue* PrefService::GetDictionary(const char* path) const { 448 DCHECK(CalledOnValidThread()); 449 450 const Preference* pref = FindPreference(path); 451 if (!pref) { 452 NOTREACHED() << "Trying to read an unregistered pref: " << path; 453 return NULL; 454 } 455 const Value* value = pref->GetValue(); 456 if (value->GetType() != Value::TYPE_DICTIONARY) { 457 NOTREACHED(); 458 return NULL; 459 } 460 return static_cast<const DictionaryValue*>(value); 461} 462 463const ListValue* PrefService::GetList(const char* path) const { 464 DCHECK(CalledOnValidThread()); 465 466 const Preference* pref = FindPreference(path); 467 if (!pref) { 468 NOTREACHED() << "Trying to read an unregistered pref: " << path; 469 return NULL; 470 } 471 const Value* value = pref->GetValue(); 472 if (value->GetType() != Value::TYPE_LIST) { 473 NOTREACHED(); 474 return NULL; 475 } 476 return static_cast<const ListValue*>(value); 477} 478 479void PrefService::AddPrefObserver(const char* path, 480 NotificationObserver* obs) { 481 pref_notifier_->AddPrefObserver(path, obs); 482} 483 484void PrefService::RemovePrefObserver(const char* path, 485 NotificationObserver* obs) { 486 pref_notifier_->RemovePrefObserver(path, obs); 487} 488 489void PrefService::RegisterPreference(const char* path, Value* default_value) { 490 DCHECK(CalledOnValidThread()); 491 492 // The main code path takes ownership, but most don't. We'll be safe. 493 scoped_ptr<Value> scoped_value(default_value); 494 495 if (FindPreference(path)) { 496 NOTREACHED() << "Tried to register duplicate pref " << path; 497 return; 498 } 499 500 Value::ValueType orig_type = default_value->GetType(); 501 DCHECK(orig_type != Value::TYPE_NULL && orig_type != Value::TYPE_BINARY) << 502 "invalid preference type: " << orig_type; 503 504 // Hand off ownership. 505 default_store_->SetDefaultValue(path, scoped_value.release()); 506} 507 508void PrefService::ClearPref(const char* path) { 509 DCHECK(CalledOnValidThread()); 510 511 const Preference* pref = FindPreference(path); 512 if (!pref) { 513 NOTREACHED() << "Trying to clear an unregistered pref: " << path; 514 return; 515 } 516 user_pref_store_->RemoveValue(path); 517} 518 519void PrefService::Set(const char* path, const Value& value) { 520 DCHECK(CalledOnValidThread()); 521 522 const Preference* pref = FindPreference(path); 523 if (!pref) { 524 NOTREACHED() << "Trying to write an unregistered pref: " << path; 525 return; 526 } 527 528 if (pref->GetType() != value.GetType()) { 529 NOTREACHED() << "Trying to set pref " << path 530 << " of type " << pref->GetType() 531 << " to value of type " << value.GetType(); 532 } else { 533 user_pref_store_->SetValue(path, value.DeepCopy()); 534 } 535} 536 537void PrefService::SetBoolean(const char* path, bool value) { 538 SetUserPrefValue(path, Value::CreateBooleanValue(value)); 539} 540 541void PrefService::SetInteger(const char* path, int value) { 542 SetUserPrefValue(path, Value::CreateIntegerValue(value)); 543} 544 545void PrefService::SetDouble(const char* path, double value) { 546 SetUserPrefValue(path, Value::CreateDoubleValue(value)); 547} 548 549void PrefService::SetString(const char* path, const std::string& value) { 550 SetUserPrefValue(path, Value::CreateStringValue(value)); 551} 552 553void PrefService::SetFilePath(const char* path, const FilePath& value) { 554 SetUserPrefValue(path, base::CreateFilePathValue(value)); 555} 556 557void PrefService::SetList(const char* path, ListValue* value) { 558 SetUserPrefValue(path, value); 559} 560 561void PrefService::SetInt64(const char* path, int64 value) { 562 SetUserPrefValue(path, Value::CreateStringValue(base::Int64ToString(value))); 563} 564 565int64 PrefService::GetInt64(const char* path) const { 566 DCHECK(CalledOnValidThread()); 567 568 const Preference* pref = FindPreference(path); 569 if (!pref) { 570 NOTREACHED() << "Trying to read an unregistered pref: " << path; 571 return 0; 572 } 573 std::string result("0"); 574 bool rv = pref->GetValue()->GetAsString(&result); 575 DCHECK(rv); 576 577 int64 val; 578 base::StringToInt64(result, &val); 579 return val; 580} 581 582void PrefService::RegisterInt64Pref(const char* path, int64 default_value) { 583 RegisterPreference( 584 path, Value::CreateStringValue(base::Int64ToString(default_value))); 585} 586 587Value* PrefService::GetMutableUserPref(const char* path, 588 Value::ValueType type) { 589 CHECK(type == Value::TYPE_DICTIONARY || type == Value::TYPE_LIST); 590 DCHECK(CalledOnValidThread()); 591 DLOG_IF(WARNING, IsManagedPreference(path)) << 592 "Attempt to change managed preference " << path; 593 594 const Preference* pref = FindPreference(path); 595 if (!pref) { 596 NOTREACHED() << "Trying to get an unregistered pref: " << path; 597 return NULL; 598 } 599 if (pref->GetType() != type) { 600 NOTREACHED() << "Wrong type for GetMutableValue: " << path; 601 return NULL; 602 } 603 604 // Look for an existing preference in the user store. If it doesn't 605 // exist or isn't the correct type, create a new user preference. 606 Value* value = NULL; 607 if (user_pref_store_->GetMutableValue(path, &value) 608 != PersistentPrefStore::READ_OK || 609 !value->IsType(type)) { 610 if (type == Value::TYPE_DICTIONARY) { 611 value = new DictionaryValue; 612 } else if (type == Value::TYPE_LIST) { 613 value = new ListValue; 614 } else { 615 NOTREACHED(); 616 } 617 user_pref_store_->SetValueSilently(path, value); 618 } 619 return value; 620} 621 622void PrefService::ReportUserPrefChanged(const std::string& key) { 623 user_pref_store_->ReportValueChanged(key); 624} 625 626void PrefService::SetUserPrefValue(const char* path, Value* new_value) { 627 DCHECK(CalledOnValidThread()); 628 DLOG_IF(WARNING, IsManagedPreference(path)) << 629 "Attempt to change managed preference " << path; 630 631 const Preference* pref = FindPreference(path); 632 if (!pref) { 633 NOTREACHED() << "Trying to write an unregistered pref: " << path; 634 return; 635 } 636 if (pref->GetType() != new_value->GetType()) { 637 NOTREACHED() << "Trying to set pref " << path 638 << " of type " << pref->GetType() 639 << " to value of type " << new_value->GetType(); 640 return; 641 } 642 643 user_pref_store_->SetValue(path, new_value); 644} 645 646/////////////////////////////////////////////////////////////////////////////// 647// PrefService::Preference 648 649PrefService::Preference::Preference(const PrefService* service, 650 const char* name, 651 Value::ValueType type) 652 : name_(name), 653 type_(type), 654 pref_service_(service) { 655 DCHECK(name); 656 DCHECK(service); 657} 658 659Value::ValueType PrefService::Preference::GetType() const { 660 return type_; 661} 662 663const Value* PrefService::Preference::GetValue() const { 664 DCHECK(pref_service_->FindPreference(name_.c_str())) << 665 "Must register pref before getting its value"; 666 667 const Value* found_value = NULL; 668 if (pref_value_store()->GetValue(name_, type_, &found_value)) { 669 DCHECK(found_value->IsType(type_)); 670 return found_value; 671 } 672 673 // Every registered preference has at least a default value. 674 NOTREACHED() << "no valid value found for registered pref " << name_; 675 return NULL; 676} 677 678bool PrefService::Preference::IsManaged() const { 679 return pref_value_store()->PrefValueInManagedStore(name_.c_str()); 680} 681 682bool PrefService::Preference::HasExtensionSetting() const { 683 return pref_value_store()->PrefValueInExtensionStore(name_.c_str()); 684} 685 686bool PrefService::Preference::HasUserSetting() const { 687 return pref_value_store()->PrefValueInUserStore(name_.c_str()); 688} 689 690bool PrefService::Preference::IsExtensionControlled() const { 691 return pref_value_store()->PrefValueFromExtensionStore(name_.c_str()); 692} 693 694bool PrefService::Preference::IsUserControlled() const { 695 return pref_value_store()->PrefValueFromUserStore(name_.c_str()); 696} 697 698bool PrefService::Preference::IsDefaultValue() const { 699 return pref_value_store()->PrefValueFromDefaultStore(name_.c_str()); 700} 701 702bool PrefService::Preference::IsUserModifiable() const { 703 return pref_value_store()->PrefValueUserModifiable(name_.c_str()); 704} 705 706bool PrefService::Preference::IsExtensionModifiable() const { 707 return pref_value_store()->PrefValueExtensionModifiable(name_.c_str()); 708} 709