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