Index: chrome/browser/pref_service.cc |
diff --git a/chrome/browser/pref_service.cc b/chrome/browser/pref_service.cc |
index d30b5448446cef0411cbfabb450833e42a9315b8..862789300b9698aefc22bc59e9fe12107cef5335 100644 |
--- a/chrome/browser/pref_service.cc |
+++ b/chrome/browser/pref_service.cc |
@@ -8,6 +8,7 @@ |
#include <string> |
#include "app/l10n_util.h" |
+#include "base/histogram.h" |
#include "base/logging.h" |
#include "base/message_loop.h" |
#include "base/stl_util-inl.h" |
@@ -15,8 +16,10 @@ |
#include "base/sys_string_conversions.h" |
#include "base/utf_string_conversions.h" |
#include "build/build_config.h" |
+#include "chrome/browser/chrome_thread.h" |
#include "chrome/common/json_value_serializer.h" |
#include "chrome/common/notification_service.h" |
+#include "grit/chromium_strings.h" |
#include "grit/generated_resources.h" |
namespace { |
@@ -62,12 +65,21 @@ Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) { |
return Value::CreateNullValue(); |
} |
+// Forwards a notification after a PostMessage so that we can wait for the |
+// MessageLoop to run. |
+void NotifyReadError(PrefService* pref, int message_id) { |
+ Source<PrefService> source(pref); |
+ NotificationService::current()->Notify(NotificationType::PROFILE_ERROR, |
+ source, Details<int>(&message_id)); |
+} |
+ |
} // namespace |
PrefService::PrefService(const FilePath& pref_filename) |
: persistent_(new DictionaryValue), |
- writer_(pref_filename) { |
- ReloadPersistentPrefs(); |
+ writer_(pref_filename), |
+ read_only_(false) { |
+ InitFromDisk(); |
} |
PrefService::~PrefService() { |
@@ -88,21 +100,82 @@ PrefService::~PrefService() { |
pref_observers_.end()); |
pref_observers_.clear(); |
- if (writer_.HasPendingWrite()) |
+ if (writer_.HasPendingWrite() && !read_only_) |
writer_.DoScheduledWrite(); |
} |
+void PrefService::InitFromDisk() { |
+ PrefReadError error = LoadPersistentPrefs(); |
+ if (error == PREF_READ_ERROR_NONE) |
+ return; |
+ |
+ // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for |
+ // an example problem that this can cause. |
+ // Do some diagnosis and try to avoid losing data. |
+ int message_id = 0; |
+ if (error <= PREF_READ_ERROR_JSON_TYPE) { |
+ // JSON errors indicate file corruption of some sort. |
+ // It's possible the user hand-edited the file, so don't clobber it yet. |
+ // Give them a chance to recover the file. |
+ // TODO(erikkay) maybe we should just move it aside and continue. |
+ read_only_ = true; |
+ message_id = IDS_PREFERENCES_CORRUPT_ERROR; |
+ } if (error == PREF_READ_ERROR_NO_FILE) { |
+ // If the file just doesn't exist, maybe this is first run. In any case |
+ // there's no harm in writing out default prefs in this case. |
+ } else { |
+ // If the file exists but is simply unreadable, put the file into a state |
+ // where we don't try to save changes. Otherwise, we could clobber the |
+ // existing prefs. |
+ read_only_ = true; |
+ message_id = IDS_PREFERENCES_UNREADABLE_ERROR; |
+ } |
+ |
+ if (message_id) { |
+ ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, |
+ NewRunnableFunction(&NotifyReadError, this, message_id)); |
+ } |
+ UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error, 20); |
+} |
+ |
bool PrefService::ReloadPersistentPrefs() { |
- DCHECK(CalledOnValidThread()); |
+ return (LoadPersistentPrefs() == PREF_READ_ERROR_NONE); |
+} |
+PrefService::PrefReadError PrefService::LoadPersistentPrefs() { |
+ DCHECK(CalledOnValidThread()); |
JSONFileValueSerializer serializer(writer_.path()); |
- scoped_ptr<Value> root(serializer.Deserialize(NULL)); |
- if (!root.get()) |
- return false; |
+ |
+ int error_code; |
+ std::string error_msg; |
+ scoped_ptr<Value> root(serializer.Deserialize(&error_code, &error_msg)); |
+ if (!root.get()) { |
+ PLOG(ERROR) << "Error reading Preferences: " << error_msg << " " << |
+ writer_.path().value(); |
+ PrefReadError pref_error; |
+ switch (error_code) { |
+ case JSONFileValueSerializer::JSON_ACCESS_DENIED: |
+ pref_error = PREF_READ_ERROR_ACCESS_DENIED; |
+ break; |
+ case JSONFileValueSerializer::JSON_CANNOT_READ_FILE: |
+ pref_error = PREF_READ_ERROR_FILE_OTHER; |
+ break; |
+ case JSONFileValueSerializer::JSON_FILE_LOCKED: |
+ pref_error = PREF_READ_ERROR_FILE_LOCKED; |
+ break; |
+ case JSONFileValueSerializer::JSON_NO_SUCH_FILE: |
+ pref_error = PREF_READ_ERROR_NO_FILE; |
+ break; |
+ default: |
+ pref_error = PREF_READ_ERROR_JSON_PARSE; |
+ break; |
+ } |
+ return pref_error; |
+ } |
// Preferences should always have a dictionary root. |
if (!root->IsType(Value::TYPE_DICTIONARY)) |
- return false; |
+ return PREF_READ_ERROR_JSON_TYPE; |
persistent_.reset(static_cast<DictionaryValue*>(root.release())); |
for (PreferenceSet::iterator it = prefs_.begin(); |
@@ -110,7 +183,7 @@ bool PrefService::ReloadPersistentPrefs() { |
(*it)->root_pref_ = persistent_.get(); |
} |
- return true; |
+ return PREF_READ_ERROR_NONE; |
} |
bool PrefService::SavePersistentPrefs() { |
@@ -120,12 +193,20 @@ bool PrefService::SavePersistentPrefs() { |
if (!SerializeData(&data)) |
return false; |
+ // Lie about our ability to save. |
+ if (read_only_) |
+ return true; |
+ |
writer_.WriteNow(data); |
return true; |
} |
void PrefService::ScheduleSavePersistentPrefs() { |
DCHECK(CalledOnValidThread()); |
+ |
+ if (read_only_) |
+ return; |
+ |
writer_.ScheduleWrite(this); |
} |