blob: 038f6331b6a88289d459af13f0e21d07494f3c92 [file] [log] [blame]
[email protected]3826fed2011-03-25 10:59:561// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]277404c22010-04-22 13:09:452// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]ea587b02010-05-21 15:01:355#include "chrome/common/json_pref_store.h"
[email protected]277404c22010-04-22 13:09:456
7#include <algorithm>
8
[email protected]844a1002011-04-19 11:37:219#include "base/bind.h"
10#include "base/callback.h"
[email protected]277404c22010-04-22 13:09:4511#include "base/file_util.h"
[email protected]844a1002011-04-19 11:37:2112#include "base/memory/ref_counted.h"
[email protected]277404c22010-04-22 13:09:4513#include "base/values.h"
[email protected]844a1002011-04-19 11:37:2114#include "content/browser/browser_thread.h"
[email protected]c3113022011-04-16 03:26:3015#include "content/common/json_value_serializer.h"
[email protected]277404c22010-04-22 13:09:4516
17namespace {
18
19// Some extensions we'll tack on to copies of the Preferences files.
20const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
21
[email protected]844a1002011-04-19 11:37:2122// 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
[email protected]277404c22010-04-22 13:09:45126} // namespace
127
[email protected]ea587b02010-05-21 15:01:35128JsonPrefStore::JsonPrefStore(const FilePath& filename,
129 base::MessageLoopProxy* file_message_loop_proxy)
[email protected]277404c22010-04-22 13:09:45130 : path_(filename),
131 prefs_(new DictionaryValue()),
132 read_only_(false),
[email protected]ea587b02010-05-21 15:01:35133 writer_(filename, file_message_loop_proxy) {
[email protected]6658ca82010-05-20 18:20:29134}
[email protected]277404c22010-04-22 13:09:45135
136JsonPrefStore::~JsonPrefStore() {
[email protected]3826fed2011-03-25 10:59:56137 CommitPendingWrite();
[email protected]277404c22010-04-22 13:09:45138}
139
[email protected]f2d1f612010-12-09 15:10:17140PrefStore::ReadResult JsonPrefStore::GetValue(const std::string& key,
[email protected]68bf41a2011-03-25 16:38:31141 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;
[email protected]f2d1f612010-12-09 15:10:17148}
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
[email protected]68bf41a2011-03-25 16:38:31158PrefStore::ReadResult JsonPrefStore::GetMutableValue(const std::string& key,
159 Value** result) {
160 return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE;
161}
162
[email protected]f2d1f612010-12-09 15:10:17163void 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
[email protected]ddb1e5a2010-12-13 20:10:45189bool JsonPrefStore::ReadOnly() const {
190 return read_only_;
191}
192
[email protected]844a1002011-04-19 11:37:21193void 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
[email protected]f2d1f612010-12-09 15:10:17243PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
[email protected]844a1002011-04-19 11:37:21244 delegate_ = NULL;
245
[email protected]fd6159a2010-09-03 09:38:39246 if (path_.empty()) {
247 read_only_ = true;
248 return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
249 }
[email protected]277404c22010-04-22 13:09:45250
251 int error_code = 0;
252 std::string error_msg;
[email protected]844a1002011-04-19 11:37:21253
254 JSONFileValueSerializer serializer(path_);
[email protected]277404c22010-04-22 13:09:45255 scoped_ptr<Value> value(serializer.Deserialize(&error_code, &error_msg));
[email protected]277404c22010-04-22 13:09:45256
[email protected]844a1002011-04-19 11:37:21257 PersistentPrefStore::PrefReadError error;
258 FileThreadDeserializer::HandleErrors(value.get(),
259 path_,
260 error_code,
261 error_msg,
262 &error);
[email protected]277404c22010-04-22 13:09:45263
[email protected]844a1002011-04-19 11:37:21264 OnFileRead(value.release(), error, false);
[email protected]277404c22010-04-22 13:09:45265
[email protected]844a1002011-04-19 11:37:21266 return error;
[email protected]277404c22010-04-22 13:09:45267}
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
[email protected]3826fed2011-03-25 10:59:56289void JsonPrefStore::CommitPendingWrite() {
290 if (writer_.HasPendingWrite() && !read_only_)
291 writer_.DoScheduledWrite();
292}
293
[email protected]f89ee342011-03-07 09:28:27294void JsonPrefStore::ReportValueChanged(const std::string& key) {
295 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
296}
297
[email protected]277404c22010-04-22 13:09:45298bool 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}