json_pref_store.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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/common/json_pref_store.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/file_util.h"
12#include "base/memory/ref_counted.h"
13#include "base/values.h"
14#include "content/browser/browser_thread.h"
15#include "content/common/json_value_serializer.h"
16
17namespace {
18
19// Some extensions we'll tack on to copies of the Preferences files.
20const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
21
22// Differentiates file loading between UI and FILE threads.
23class FileThreadDeserializer
24    : public base::RefCountedThreadSafe<FileThreadDeserializer> {
25 public:
26  explicit FileThreadDeserializer(JsonPrefStore* delegate)
27      : delegate_(delegate) {
28  }
29
30  void Start(const FilePath& path) {
31    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
32    BrowserThread::PostTask(
33        BrowserThread::FILE,
34        FROM_HERE,
35        NewRunnableMethod(this,
36                          &FileThreadDeserializer::ReadFileAndReport,
37                          path));
38  }
39
40  // Deserializes JSON on the FILE thread.
41  void ReadFileAndReport(const FilePath& path) {
42    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43
44    int error_code;
45    std::string error_msg;
46    JSONFileValueSerializer serializer(path);
47    value_.reset(serializer.Deserialize(&error_code, &error_msg));
48
49    HandleErrors(value_.get(), path, error_code, error_msg, &error_);
50
51    no_dir_ = !file_util::PathExists(path.DirName());
52
53    BrowserThread::PostTask(
54        BrowserThread::UI,
55        FROM_HERE,
56        NewRunnableMethod(this, &FileThreadDeserializer::ReportOnUIThread));
57  }
58
59  // Reports deserialization result on the UI thread.
60  void ReportOnUIThread() {
61    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
62    delegate_->OnFileRead(value_.release(), error_, no_dir_);
63  }
64
65  static void HandleErrors(const Value* value,
66                           const FilePath& path,
67                           int error_code,
68                           const std::string& error_msg,
69                           PersistentPrefStore::PrefReadError* error);
70
71 private:
72  friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
73
74  bool no_dir_;
75  PersistentPrefStore::PrefReadError error_;
76  scoped_ptr<Value> value_;
77  scoped_refptr<JsonPrefStore> delegate_;
78};
79
80// static
81void FileThreadDeserializer::HandleErrors(
82    const Value* value,
83    const FilePath& path,
84    int error_code,
85    const std::string& error_msg,
86    PersistentPrefStore::PrefReadError* error) {
87  *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
88  if (!value) {
89    DLOG(ERROR) << "Error while loading JSON file: " << error_msg;
90    switch (error_code) {
91      case JSONFileValueSerializer::JSON_ACCESS_DENIED:
92        *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
93        break;
94      case JSONFileValueSerializer::JSON_CANNOT_READ_FILE:
95        *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
96        break;
97      case JSONFileValueSerializer::JSON_FILE_LOCKED:
98        *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
99        break;
100      case JSONFileValueSerializer::JSON_NO_SUCH_FILE:
101        *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
102        break;
103      default:
104        *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
105        // JSON errors indicate file corruption of some sort.
106        // Since the file is corrupt, move it to the side and continue with
107        // empty preferences.  This will result in them losing their settings.
108        // We keep the old file for possible support and debugging assistance
109        // as well as to detect if they're seeing these errors repeatedly.
110        // TODO(erikkay) Instead, use the last known good file.
111        FilePath bad = path.ReplaceExtension(kBadExtension);
112
113        // If they've ever had a parse error before, put them in another bucket.
114        // TODO(erikkay) if we keep this error checking for very long, we may
115        // want to differentiate between recent and long ago errors.
116        if (file_util::PathExists(bad))
117          *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT;
118        file_util::Move(path, bad);
119        break;
120    }
121  } else if (!value->IsType(Value::TYPE_DICTIONARY)) {
122    *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
123  }
124}
125
126}  // namespace
127
128JsonPrefStore::JsonPrefStore(const FilePath& filename,
129                             base::MessageLoopProxy* file_message_loop_proxy)
130    : path_(filename),
131      prefs_(new DictionaryValue()),
132      read_only_(false),
133      writer_(filename, file_message_loop_proxy) {
134}
135
136JsonPrefStore::~JsonPrefStore() {
137  CommitPendingWrite();
138}
139
140PrefStore::ReadResult JsonPrefStore::GetValue(const std::string& key,
141                                              const Value** result) const {
142  Value* tmp = NULL;
143  if (prefs_->Get(key, &tmp)) {
144    *result = tmp;
145    return READ_OK;
146  }
147  return READ_NO_VALUE;
148}
149
150void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
151  observers_.AddObserver(observer);
152}
153
154void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
155  observers_.RemoveObserver(observer);
156}
157
158PrefStore::ReadResult JsonPrefStore::GetMutableValue(const std::string& key,
159                                                     Value** result) {
160  return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE;
161}
162
163void JsonPrefStore::SetValue(const std::string& key, Value* value) {
164  DCHECK(value);
165  scoped_ptr<Value> new_value(value);
166  Value* old_value = NULL;
167  prefs_->Get(key, &old_value);
168  if (!old_value || !value->Equals(old_value)) {
169    prefs_->Set(key, new_value.release());
170    FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
171  }
172}
173
174void JsonPrefStore::SetValueSilently(const std::string& key, Value* value) {
175  DCHECK(value);
176  scoped_ptr<Value> new_value(value);
177  Value* old_value = NULL;
178  prefs_->Get(key, &old_value);
179  if (!old_value || !value->Equals(old_value))
180    prefs_->Set(key, new_value.release());
181}
182
183void JsonPrefStore::RemoveValue(const std::string& key) {
184  if (prefs_->Remove(key, NULL)) {
185    FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
186  }
187}
188
189bool JsonPrefStore::ReadOnly() const {
190  return read_only_;
191}
192
193void JsonPrefStore::OnFileRead(Value* value_owned,
194                               PersistentPrefStore::PrefReadError error,
195                               bool no_dir) {
196  scoped_ptr<Value> value(value_owned);
197  switch (error) {
198    case PREF_READ_ERROR_ACCESS_DENIED:
199    case PREF_READ_ERROR_FILE_OTHER:
200    case PREF_READ_ERROR_FILE_LOCKED:
201    case PREF_READ_ERROR_JSON_TYPE:
202      read_only_ = true;
203      break;
204    case PREF_READ_ERROR_NONE:
205      DCHECK(value.get());
206      prefs_.reset(static_cast<DictionaryValue*>(value.release()));
207      break;
208    case PREF_READ_ERROR_NO_FILE:
209      // If the file just doesn't exist, maybe this is first run.  In any case
210      // there's no harm in writing out default prefs in this case.
211      break;
212    case PREF_READ_ERROR_JSON_PARSE:
213    case PREF_READ_ERROR_JSON_REPEAT:
214      break;
215    default:
216      NOTREACHED() << "Unknown error: " << error;
217  }
218
219  if (delegate_)
220    delegate_->OnPrefsRead(error, no_dir);
221}
222
223void JsonPrefStore::ReadPrefs(Delegate* delegate) {
224  DCHECK(delegate);
225  delegate_ = delegate;
226
227  if (path_.empty()) {
228    read_only_ = true;
229    delegate_->OnPrefsRead(PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
230    return;
231  }
232
233  // This guarantees that class will not be deleted while JSON is readed.
234  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
235
236  // Start async reading of the preferences file. It will delete itself
237  // in the end.
238  scoped_refptr<FileThreadDeserializer> deserializer(
239      new FileThreadDeserializer(this));
240  deserializer->Start(path_);
241}
242
243PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
244  delegate_ = NULL;
245
246  if (path_.empty()) {
247    read_only_ = true;
248    return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
249  }
250
251  int error_code = 0;
252  std::string error_msg;
253
254  JSONFileValueSerializer serializer(path_);
255  scoped_ptr<Value> value(serializer.Deserialize(&error_code, &error_msg));
256
257  PersistentPrefStore::PrefReadError error;
258  FileThreadDeserializer::HandleErrors(value.get(),
259                                       path_,
260                                       error_code,
261                                       error_msg,
262                                       &error);
263
264  OnFileRead(value.release(), error, false);
265
266  return error;
267}
268
269bool JsonPrefStore::WritePrefs() {
270  std::string data;
271  if (!SerializeData(&data))
272    return false;
273
274  // Lie about our ability to save.
275  if (read_only_)
276    return true;
277
278  writer_.WriteNow(data);
279  return true;
280}
281
282void JsonPrefStore::ScheduleWritePrefs() {
283  if (read_only_)
284    return;
285
286  writer_.ScheduleWrite(this);
287}
288
289void JsonPrefStore::CommitPendingWrite() {
290  if (writer_.HasPendingWrite() && !read_only_)
291    writer_.DoScheduledWrite();
292}
293
294void JsonPrefStore::ReportValueChanged(const std::string& key) {
295  FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
296}
297
298bool JsonPrefStore::SerializeData(std::string* output) {
299  // TODO(tc): Do we want to prune webkit preferences that match the default
300  // value?
301  JSONStringValueSerializer serializer(output);
302  serializer.set_pretty_print(true);
303  scoped_ptr<DictionaryValue> copy(prefs_->DeepCopyWithoutEmptyChildren());
304  return serializer.Serialize(*(copy.get()));
305}
306