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
17 namespace {
18
19 // Some extensions we'll tack on to copies of the Preferences files.
20 const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad");
21
22 // Differentiates file loading between UI and FILE threads.
23 class FileThreadDeserializer
24 : public base::RefCountedThreadSafe<FileThreadDeserializer> {
25 public:
FileThreadDeserializer(JsonPrefStore * delegate)26 explicit FileThreadDeserializer(JsonPrefStore* delegate)
27 : delegate_(delegate) {
28 }
29
Start(const FilePath & path)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.
ReadFileAndReport(const FilePath & path)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.
ReportOnUIThread()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
HandleErrors(const Value * value,const FilePath & path,int error_code,const std::string & error_msg,PersistentPrefStore::PrefReadError * error)81 void 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
JsonPrefStore(const FilePath & filename,base::MessageLoopProxy * file_message_loop_proxy)128 JsonPrefStore::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
~JsonPrefStore()136 JsonPrefStore::~JsonPrefStore() {
137 CommitPendingWrite();
138 }
139
GetValue(const std::string & key,const Value ** result) const140 PrefStore::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
AddObserver(PrefStore::Observer * observer)150 void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
151 observers_.AddObserver(observer);
152 }
153
RemoveObserver(PrefStore::Observer * observer)154 void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
155 observers_.RemoveObserver(observer);
156 }
157
GetMutableValue(const std::string & key,Value ** result)158 PrefStore::ReadResult JsonPrefStore::GetMutableValue(const std::string& key,
159 Value** result) {
160 return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE;
161 }
162
SetValue(const std::string & key,Value * value)163 void 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
SetValueSilently(const std::string & key,Value * value)174 void 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
RemoveValue(const std::string & key)183 void JsonPrefStore::RemoveValue(const std::string& key) {
184 if (prefs_->Remove(key, NULL)) {
185 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
186 }
187 }
188
ReadOnly() const189 bool JsonPrefStore::ReadOnly() const {
190 return read_only_;
191 }
192
OnFileRead(Value * value_owned,PersistentPrefStore::PrefReadError error,bool no_dir)193 void 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
ReadPrefs(Delegate * delegate)223 void 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
ReadPrefs()243 PersistentPrefStore::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
WritePrefs()269 bool 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
ScheduleWritePrefs()282 void JsonPrefStore::ScheduleWritePrefs() {
283 if (read_only_)
284 return;
285
286 writer_.ScheduleWrite(this);
287 }
288
CommitPendingWrite()289 void JsonPrefStore::CommitPendingWrite() {
290 if (writer_.HasPendingWrite() && !read_only_)
291 writer_.DoScheduledWrite();
292 }
293
ReportValueChanged(const std::string & key)294 void JsonPrefStore::ReportValueChanged(const std::string& key) {
295 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
296 }
297
SerializeData(std::string * output)298 bool 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