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